@dcrays/dcgchat-test 0.3.2 → 0.3.3
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/package.json +1 -1
- package/src/bot.ts +101 -26
- package/src/channel.ts +3 -2
- package/src/gateway/socket.ts +2 -4
- package/src/monitor.ts +9 -5
- package/src/tool.ts +53 -63
- package/src/transport.ts +2 -2
- package/src/types.ts +1 -0
- package/src/utils/constant.ts +2 -2
- package/src/utils/global.ts +28 -5
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -3,13 +3,20 @@ import path from 'node:path'
|
|
|
3
3
|
import type { ReplyPayload } from 'openclaw/plugin-sdk'
|
|
4
4
|
import { createReplyPrefixContext } from 'openclaw/plugin-sdk'
|
|
5
5
|
import type { InboundMessage } from './types.js'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
clearSentMediaKeys,
|
|
8
|
+
getDcgchatRuntime,
|
|
9
|
+
getOpenClawConfig,
|
|
10
|
+
getSessionKey,
|
|
11
|
+
getWorkspaceDir,
|
|
12
|
+
setMsgStatus
|
|
13
|
+
} from './utils/global.js'
|
|
7
14
|
import { resolveAccount, sendDcgchatMedia } from './channel.js'
|
|
8
15
|
import { generateSignUrl } from './request/api.js'
|
|
9
16
|
import { extractMobookFiles } from './utils/searchFile.js'
|
|
10
|
-
import { sendChunk, sendFinal, sendText as sendTextMsg, sendError } from './transport.js'
|
|
17
|
+
import { sendChunk, sendFinal, sendText as sendTextMsg, sendError, wsSendRaw, sendText } from './transport.js'
|
|
11
18
|
import { dcgLogger } from './utils/log.js'
|
|
12
|
-
import { channelInfo, systemCommand, interruptCommand, ENV } from './utils/constant.js'
|
|
19
|
+
import { channelInfo, systemCommand, interruptCommand, ENV, ignoreToolCommand } from './utils/constant.js'
|
|
13
20
|
import { sendMessageToGateway } from './gateway/socket.js'
|
|
14
21
|
import { getEffectiveMsgParams, setParamsMessage } from './utils/params.js'
|
|
15
22
|
|
|
@@ -24,6 +31,18 @@ type TFileInfo = { name: string; url: string }
|
|
|
24
31
|
|
|
25
32
|
const mediaMaxBytes = 300 * 1024 * 1024
|
|
26
33
|
|
|
34
|
+
/** 当前会话最近一次 agent run 的 runId(供 chat.abort 精确打断;无则仅传 sessionKey 仍会中止该会话全部活动运行) */
|
|
35
|
+
const activeRunIdBySessionKey = new Map<string, string>()
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 用户在该 sessionKey 上触发打断后,旧 run 的流式/投递不再下发;与 sessionKey 一一对应,支持多会话。
|
|
39
|
+
* 清除时机:① 下一条非打断用户消息开始处理时;② 旧 run 收尾到 mobook 段时若仍抑制则跳过并发后删除。
|
|
40
|
+
*/
|
|
41
|
+
const sessionStreamSuppressed = new Set<string>()
|
|
42
|
+
|
|
43
|
+
/** 各 sessionKey 当前轮回复的流式分片序号(仅统计实际下发的文本 chunk);每轮非打断消息开始时置 0 */
|
|
44
|
+
const streamChunkIdxBySessionKey = new Map<string, number>()
|
|
45
|
+
|
|
27
46
|
/** Active LLM generation abort controllers, keyed by conversationId */
|
|
28
47
|
// const activeGenerations = new Map<string, AbortController>()
|
|
29
48
|
|
|
@@ -144,8 +163,8 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
144
163
|
const embeddedAgentId = extractAgentIdFromConversationId(conversationId)
|
|
145
164
|
|
|
146
165
|
const effectiveAgentId = embeddedAgentId ?? route.agentId
|
|
147
|
-
const effectiveSessionKey =
|
|
148
|
-
|
|
166
|
+
const effectiveSessionKey = getSessionKey(msg.content, account.accountId)
|
|
167
|
+
console.log('🚀 ~ handleDcgchatMessage ~ effectiveSessionKey:', effectiveSessionKey)
|
|
149
168
|
|
|
150
169
|
setParamsMessage(effectiveSessionKey, {
|
|
151
170
|
userId: msg._userId,
|
|
@@ -168,6 +187,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
168
187
|
if (finalSent) return
|
|
169
188
|
finalSent = true
|
|
170
189
|
sendFinal(outboundCtx)
|
|
190
|
+
setMsgStatus(effectiveSessionKey, 'finished')
|
|
171
191
|
}
|
|
172
192
|
|
|
173
193
|
const text = msg.content.text?.trim()
|
|
@@ -243,6 +263,7 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
243
263
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(config, route.agentId),
|
|
244
264
|
onReplyStart: async () => {},
|
|
245
265
|
deliver: async (payload: ReplyPayload, info) => {
|
|
266
|
+
if (sessionStreamSuppressed.has(effectiveSessionKey)) return
|
|
246
267
|
const mediaList = resolveReplyMediaList(payload)
|
|
247
268
|
for (const mediaUrl of mediaList) {
|
|
248
269
|
const key = getMediaKey(mediaUrl)
|
|
@@ -252,15 +273,29 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
252
273
|
}
|
|
253
274
|
},
|
|
254
275
|
onError: (err: unknown, info: { kind: string }) => {
|
|
276
|
+
activeRunIdBySessionKey.delete(effectiveSessionKey)
|
|
277
|
+
streamChunkIdxBySessionKey.delete(effectiveSessionKey)
|
|
278
|
+
if (sessionStreamSuppressed.has(effectiveSessionKey)) {
|
|
279
|
+
dcgLogger(`${info.kind} reply failed (stream suppressed): ${String(err)}`, 'error')
|
|
280
|
+
return
|
|
281
|
+
}
|
|
255
282
|
safeSendFinal()
|
|
256
283
|
dcgLogger(`${info.kind} reply failed: ${String(err)}`, 'error')
|
|
257
284
|
},
|
|
258
285
|
onIdle: () => {
|
|
286
|
+
activeRunIdBySessionKey.delete(effectiveSessionKey)
|
|
287
|
+
streamChunkIdxBySessionKey.delete(effectiveSessionKey)
|
|
288
|
+
if (sessionStreamSuppressed.has(effectiveSessionKey)) return
|
|
259
289
|
safeSendFinal()
|
|
260
290
|
}
|
|
261
291
|
})
|
|
262
292
|
|
|
263
293
|
try {
|
|
294
|
+
if (!interruptCommand.includes(text?.trim())) {
|
|
295
|
+
sessionStreamSuppressed.delete(effectiveSessionKey)
|
|
296
|
+
streamChunkIdxBySessionKey.set(effectiveSessionKey, 0)
|
|
297
|
+
}
|
|
298
|
+
|
|
264
299
|
if (systemCommand.includes(text?.trim())) {
|
|
265
300
|
dcgLogger(`dispatching /new`)
|
|
266
301
|
await core.channel.reply.dispatchReplyFromConfig({
|
|
@@ -269,21 +304,48 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
269
304
|
dispatcher,
|
|
270
305
|
replyOptions: {
|
|
271
306
|
...replyOptions,
|
|
272
|
-
onModelSelected: prefixContext.onModelSelected
|
|
307
|
+
onModelSelected: prefixContext.onModelSelected,
|
|
308
|
+
onAgentRunStart: (runId) => {
|
|
309
|
+
activeRunIdBySessionKey.set(effectiveSessionKey, runId)
|
|
310
|
+
}
|
|
273
311
|
}
|
|
274
312
|
})
|
|
275
313
|
} else if (interruptCommand.includes(text?.trim())) {
|
|
276
314
|
dcgLogger(`interrupt command: ${text}`)
|
|
315
|
+
sendText('会话已终止', outboundCtx)
|
|
316
|
+
safeSendFinal()
|
|
317
|
+
sessionStreamSuppressed.add(effectiveSessionKey)
|
|
318
|
+
const runId = activeRunIdBySessionKey.get(effectiveSessionKey)
|
|
277
319
|
sendMessageToGateway(
|
|
278
320
|
JSON.stringify({
|
|
279
321
|
method: 'chat.abort',
|
|
280
|
-
params: {
|
|
322
|
+
params: {
|
|
323
|
+
sessionKey: effectiveSessionKey,
|
|
324
|
+
...(runId ? { runId } : {})
|
|
325
|
+
}
|
|
281
326
|
})
|
|
282
327
|
)
|
|
283
|
-
|
|
328
|
+
if (runId) activeRunIdBySessionKey.delete(effectiveSessionKey)
|
|
284
329
|
return
|
|
285
330
|
} else {
|
|
286
331
|
dcgLogger(`dispatching to agent (session=${route.sessionKey})`)
|
|
332
|
+
const params = getEffectiveMsgParams(effectiveSessionKey)
|
|
333
|
+
if (!ignoreToolCommand.includes(text?.trim())) {
|
|
334
|
+
// message_received 没有 sessionKey 前置到bot中执行
|
|
335
|
+
wsSendRaw(params, {
|
|
336
|
+
is_finish: -1,
|
|
337
|
+
tool_call_id: Date.now().toString(),
|
|
338
|
+
is_cover: 0,
|
|
339
|
+
thinking_content: JSON.stringify({
|
|
340
|
+
type: 'message_received',
|
|
341
|
+
specialIdentification: 'dcgchat_tool_call_special_identification',
|
|
342
|
+
toolName: '',
|
|
343
|
+
callId: Date.now().toString(),
|
|
344
|
+
params: ''
|
|
345
|
+
}),
|
|
346
|
+
response: ''
|
|
347
|
+
})
|
|
348
|
+
}
|
|
287
349
|
await core.channel.reply.dispatchReplyFromConfig({
|
|
288
350
|
ctx: ctxPayload,
|
|
289
351
|
cfg: config,
|
|
@@ -292,7 +354,15 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
292
354
|
...replyOptions,
|
|
293
355
|
// abortSignal: genSignal,
|
|
294
356
|
onModelSelected: prefixContext.onModelSelected,
|
|
357
|
+
onAgentRunStart: (runId) => {
|
|
358
|
+
activeRunIdBySessionKey.set(effectiveSessionKey, runId)
|
|
359
|
+
},
|
|
295
360
|
onPartialReply: async (payload: ReplyPayload) => {
|
|
361
|
+
if (sessionStreamSuppressed.has(effectiveSessionKey)) return
|
|
362
|
+
console.log(
|
|
363
|
+
'🚀 ~ handleDcgchatMessage ~ sessionStreamSuppressed.has(effectiveSessionKey):',
|
|
364
|
+
sessionStreamSuppressed.has(effectiveSessionKey)
|
|
365
|
+
)
|
|
296
366
|
// Accumulate full text
|
|
297
367
|
if (payload.text) {
|
|
298
368
|
completeText = payload.text
|
|
@@ -303,8 +373,10 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
303
373
|
? payload.text.slice(streamedTextLen)
|
|
304
374
|
: payload.text
|
|
305
375
|
if (delta.trim()) {
|
|
306
|
-
|
|
307
|
-
|
|
376
|
+
const prev = streamChunkIdxBySessionKey.get(effectiveSessionKey) ?? 0
|
|
377
|
+
streamChunkIdxBySessionKey.set(effectiveSessionKey, prev + 1)
|
|
378
|
+
sendChunk(delta, outboundCtx, prev)
|
|
379
|
+
dcgLogger(`[stream]: chunkIdx=${prev} len=${delta.length} user=${msg._userId} ${delta.slice(0, 100)}`)
|
|
308
380
|
}
|
|
309
381
|
streamedTextLen = payload.text.length
|
|
310
382
|
}
|
|
@@ -342,28 +414,31 @@ export async function handleDcgchatMessage(msg: InboundMessage, accountId: strin
|
|
|
342
414
|
dcgLogger(` markRunComplete||markRunComplete error: ${String(err)}`, 'error')
|
|
343
415
|
}
|
|
344
416
|
if (![...systemCommand, ...interruptCommand].includes(text?.trim())) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
417
|
+
if (sessionStreamSuppressed.has(effectiveSessionKey)) {
|
|
418
|
+
sessionStreamSuppressed.delete(effectiveSessionKey)
|
|
419
|
+
} else {
|
|
420
|
+
for (const file of extractMobookFiles(completeText)) {
|
|
421
|
+
const candidates: string[] = [file]
|
|
422
|
+
candidates.push(path.join(getWorkspaceDir(), file))
|
|
423
|
+
candidates.push(path.join(getWorkspaceDir(), file.replace(/^\//, '')))
|
|
424
|
+
if (process.platform === 'win32') {
|
|
425
|
+
const underMobook = file.replace(/^\/mobook\//i, '').replace(/^mobook[\\/]/i, '')
|
|
426
|
+
if (underMobook) {
|
|
427
|
+
candidates.push(path.join('C:\\', 'mobook', underMobook))
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const resolved = candidates.find((p) => fs.existsSync(p))
|
|
431
|
+
if (!resolved) continue
|
|
432
|
+
try {
|
|
433
|
+
await sendDcgchatMedia({ sessionKey: effectiveSessionKey, mediaUrl: resolved, text: '' })
|
|
434
|
+
} catch (err) {
|
|
435
|
+
dcgLogger(` sendDcgchatMedia error: ${String(err)}`, 'error')
|
|
353
436
|
}
|
|
354
|
-
}
|
|
355
|
-
const resolved = candidates.find((p) => fs.existsSync(p))
|
|
356
|
-
if (!resolved) continue
|
|
357
|
-
try {
|
|
358
|
-
await sendDcgchatMedia({ sessionKey: effectiveSessionKey, mediaUrl: resolved, text: '' })
|
|
359
|
-
} catch (err) {
|
|
360
|
-
dcgLogger(` sendDcgchatMedia error: ${String(err)}`, 'error')
|
|
361
437
|
}
|
|
362
438
|
}
|
|
363
439
|
}
|
|
364
440
|
safeSendFinal()
|
|
365
441
|
clearSentMediaKeys(msg.content.message_id)
|
|
366
|
-
setMsgStatus('finished')
|
|
367
442
|
|
|
368
443
|
// Record session metadata
|
|
369
444
|
const storePath = core.channel.session.resolveStorePath(config.session?.store)
|
package/src/channel.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { ossUpload } from './request/oss.js'
|
|
|
5
5
|
import { addSentMediaKey, getOpenClawConfig, hasSentMediaKey } from './utils/global.js'
|
|
6
6
|
import { isWsOpen, mergeDefaultParams, mergeSessionParams, sendFinal, wsSendRaw } from './transport.js'
|
|
7
7
|
import { dcgLogger, setLogger } from './utils/log.js'
|
|
8
|
-
import {
|
|
8
|
+
import { getEffectiveMsgParams, getCurrentSessionKey } from './utils/params.js'
|
|
9
9
|
import { startDcgchatGatewaySocket } from './gateway/socket.js'
|
|
10
10
|
|
|
11
11
|
export type DcgchatMediaSendOptions = {
|
|
@@ -146,7 +146,8 @@ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
|
|
|
146
146
|
const merged = mergeDefaultParams({
|
|
147
147
|
agentId: ctx.accountId ?? '',
|
|
148
148
|
sessionId: ctx.to,
|
|
149
|
-
messageId: `${Date.now()}
|
|
149
|
+
messageId: `${Date.now()}`,
|
|
150
|
+
is_finish: -1
|
|
150
151
|
})
|
|
151
152
|
wsSendRaw(merged, { response: ctx.text })
|
|
152
153
|
sendFinal(merged)
|
package/src/gateway/socket.ts
CHANGED
|
@@ -230,10 +230,8 @@ export function startDcgchatGatewaySocket(): void {
|
|
|
230
230
|
socketStopped = false
|
|
231
231
|
clearReconnectTimer()
|
|
232
232
|
if (startupConnectTimer != null) return
|
|
233
|
-
startupConnectTimer =
|
|
234
|
-
|
|
235
|
-
void connectPersistentGateway()
|
|
236
|
-
}, 10000)
|
|
233
|
+
startupConnectTimer = null
|
|
234
|
+
void connectPersistentGateway()
|
|
237
235
|
}
|
|
238
236
|
|
|
239
237
|
/**
|
package/src/monitor.ts
CHANGED
|
@@ -2,12 +2,12 @@ import type { ClawdbotConfig, RuntimeEnv } from 'openclaw/plugin-sdk'
|
|
|
2
2
|
import WebSocket from 'ws'
|
|
3
3
|
import { handleDcgchatMessage } from './bot.js'
|
|
4
4
|
import { resolveAccount } from './channel.js'
|
|
5
|
-
import { setWsConnection, getOpenClawConfig, setMsgStatus } from './utils/global.js'
|
|
5
|
+
import { setWsConnection, getOpenClawConfig, setMsgStatus, getSessionKey } from './utils/global.js'
|
|
6
6
|
import type { InboundMessage } from './types.js'
|
|
7
7
|
import { installSkill, uninstallSkill } from './skill.js'
|
|
8
8
|
import { dcgLogger } from './utils/log.js'
|
|
9
|
-
import { ignoreToolCommand } from './utils/constant.js'
|
|
10
9
|
import { onDisabledCronJob, onEnabledCronJob, onRemoveCronJob, onRunCronJob } from './cron.js'
|
|
10
|
+
import { ignoreToolCommand } from './utils/constant.js'
|
|
11
11
|
|
|
12
12
|
export type MonitorDcgchatOpts = {
|
|
13
13
|
config?: ClawdbotConfig
|
|
@@ -126,10 +126,14 @@ export async function monitorDcgchatProvider(opts: MonitorDcgchatOpts): Promise<
|
|
|
126
126
|
|
|
127
127
|
if (parsed.messageType == 'openclaw_bot_chat') {
|
|
128
128
|
const msg = parsed as unknown as InboundMessage
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
// 与 monitor 原逻辑一致:工具类指令不进入 running,避免误触工具链监控
|
|
130
|
+
const effectiveSessionKey = getSessionKey(msg.content, account.accountId)
|
|
131
|
+
console.log('🚀 ~ connect ~ effectiveSessionKey:', effectiveSessionKey)
|
|
132
|
+
if (!ignoreToolCommand.includes(msg.content.text?.trim() ?? '')) {
|
|
133
|
+
setMsgStatus(effectiveSessionKey, 'running')
|
|
134
|
+
} else {
|
|
135
|
+
setMsgStatus(effectiveSessionKey, 'finished')
|
|
131
136
|
}
|
|
132
|
-
|
|
133
137
|
await handleDcgchatMessage(msg, account.accountId)
|
|
134
138
|
} else if (parsed.messageType == 'openclaw_bot_event') {
|
|
135
139
|
const { event_type, operation_type } = parsed.content ? parsed.content : ({} as Record<string, any>)
|
package/src/tool.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
|
|
2
|
-
import { getMsgStatus
|
|
2
|
+
import { getMsgStatus } from './utils/global.js'
|
|
3
3
|
import { dcgLogger } from './utils/log.js'
|
|
4
|
-
import {
|
|
4
|
+
import { sendFinal, sendText, wsSendRaw } from './transport.js'
|
|
5
5
|
import { getCurrentSessionKey, getEffectiveMsgParams } from './utils/params.js'
|
|
6
6
|
|
|
7
7
|
let toolCallId = ''
|
|
@@ -31,8 +31,10 @@ type PluginHookName =
|
|
|
31
31
|
| 'subagent_ended'
|
|
32
32
|
| 'gateway_start'
|
|
33
33
|
| 'gateway_stop'
|
|
34
|
+
|
|
35
|
+
// message_received 没有 sessionKey 前置到bot中执行
|
|
34
36
|
const eventList = [
|
|
35
|
-
{ event: 'message_received', message: '' },
|
|
37
|
+
// { event: 'message_received', message: '' },
|
|
36
38
|
// {event: 'before_model_resolve', message: ''},
|
|
37
39
|
// {event: 'before_prompt_build', message: '正在查阅背景资料,构建思考逻辑'},
|
|
38
40
|
// {event: 'before_agent_start', message: '书灵墨宝已就位,准备开始执行任务'},
|
|
@@ -50,75 +52,63 @@ const eventList = [
|
|
|
50
52
|
{ event: 'after_tool_call', message: '' }
|
|
51
53
|
]
|
|
52
54
|
|
|
53
|
-
function sendToolCallMessage(text: string, toolCallId: string, isCover: number) {
|
|
54
|
-
const ws = getWsConnection()
|
|
55
|
-
const sk = getCurrentSessionKey()
|
|
56
|
-
if (!sk) return
|
|
55
|
+
function sendToolCallMessage(sk: string, text: string, toolCallId: string, isCover: number) {
|
|
57
56
|
const params = getEffectiveMsgParams(sk)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
content: {
|
|
66
|
-
is_finish: -1,
|
|
67
|
-
bot_token: params?.botToken,
|
|
68
|
-
domain_id: params?.domainId,
|
|
69
|
-
app_id: params?.appId,
|
|
70
|
-
bot_id: params?.botId,
|
|
71
|
-
agent_id: params?.agentId,
|
|
72
|
-
tool_call_id: toolCallId,
|
|
73
|
-
is_cover: isCover,
|
|
74
|
-
thinking_content: text,
|
|
75
|
-
response: '',
|
|
76
|
-
session_id: params?.sessionId,
|
|
77
|
-
message_id: params?.messageId || Date.now().toString()
|
|
78
|
-
}
|
|
79
|
-
})
|
|
80
|
-
)
|
|
81
|
-
}
|
|
57
|
+
wsSendRaw(params, {
|
|
58
|
+
is_finish: -1,
|
|
59
|
+
tool_call_id: toolCallId,
|
|
60
|
+
is_cover: isCover,
|
|
61
|
+
thinking_content: text,
|
|
62
|
+
response: ''
|
|
63
|
+
})
|
|
82
64
|
}
|
|
83
65
|
|
|
84
66
|
export function monitoringToolMessage(api: OpenClawPluginApi) {
|
|
85
67
|
for (const item of eventList) {
|
|
86
68
|
api.on(item.event as PluginHookName, (event: any, args: any) => {
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
69
|
+
const sk = args?.sessionKey as string
|
|
70
|
+
if (sk) {
|
|
71
|
+
const status = getMsgStatus(sk)
|
|
72
|
+
if (status === 'running') {
|
|
73
|
+
if (['after_tool_call', 'before_tool_call'].includes(item.event)) {
|
|
74
|
+
const { result: _result, ...rest } = event
|
|
75
|
+
dcgLogger(`工具调用结果: ~ event:${item.event} ~ params:${JSON.stringify(rest)}`)
|
|
76
|
+
const text = JSON.stringify({
|
|
77
|
+
type: item.event,
|
|
78
|
+
specialIdentification: 'dcgchat_tool_call_special_identification',
|
|
79
|
+
callId: event.toolCallId || event.runId || Date.now().toString(),
|
|
80
|
+
...rest,
|
|
81
|
+
status: item.event === 'after_tool_call' ? 'finished' : 'running'
|
|
82
|
+
})
|
|
83
|
+
sendToolCallMessage(
|
|
84
|
+
sk,
|
|
85
|
+
text,
|
|
86
|
+
event.toolCallId || event.runId || Date.now().toString(),
|
|
87
|
+
item.event === 'after_tool_call' ? 1 : 0
|
|
88
|
+
)
|
|
89
|
+
} else if (item.event) {
|
|
90
|
+
const msgCtx = getEffectiveMsgParams(sk)
|
|
91
|
+
if (item.event === 'llm_output') {
|
|
92
|
+
if (event.lastAssistant?.errorMessage === '429-账户额度耗尽') {
|
|
93
|
+
const message = '您的积分已消耗完,您可以通过充值积分来继续使用'
|
|
94
|
+
sendText(message, msgCtx, { message_tags: { insufficient_balance: 1 }, is_finish: -1 })
|
|
95
|
+
sendFinal(msgCtx)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
111
98
|
}
|
|
99
|
+
const text = JSON.stringify({
|
|
100
|
+
type: item.event,
|
|
101
|
+
specialIdentification: 'dcgchat_tool_call_special_identification',
|
|
102
|
+
toolName: '',
|
|
103
|
+
callId: event.runId || Date.now().toString(),
|
|
104
|
+
params: item.message
|
|
105
|
+
})
|
|
106
|
+
sendToolCallMessage(sk, text, event.runId || Date.now().toString(), 0)
|
|
107
|
+
dcgLogger(`工具调用结果: ~ event:${item.event} ${status}`)
|
|
112
108
|
}
|
|
113
|
-
const text = JSON.stringify({
|
|
114
|
-
type: item.event,
|
|
115
|
-
specialIdentification: 'dcgchat_tool_call_special_identification',
|
|
116
|
-
toolName: '',
|
|
117
|
-
callId: event.runId || Date.now().toString(),
|
|
118
|
-
params: item.message
|
|
119
|
-
})
|
|
120
|
-
sendToolCallMessage(text, event.runId || Date.now().toString(), 0)
|
|
121
109
|
}
|
|
110
|
+
} else {
|
|
111
|
+
dcgLogger(`工具调用结果: ~ event:${item.event} ~ 没有sessionKey 为执行`)
|
|
122
112
|
}
|
|
123
113
|
})
|
|
124
114
|
}
|
package/src/transport.ts
CHANGED
|
@@ -158,8 +158,8 @@ export function wsSendRaw(ctx: IMsgParams, content: Record<string, unknown>): bo
|
|
|
158
158
|
return true
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
export function sendChunk(text: string, ctx: IMsgParams): boolean {
|
|
162
|
-
return wsSend(ctx, { response: text, state: 'chunk' })
|
|
161
|
+
export function sendChunk(text: string, ctx: IMsgParams, chunkIdx: number): boolean {
|
|
162
|
+
return wsSend(ctx, { response: text, state: 'chunk', chunk_idx: chunkIdx })
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export function sendFinal(ctx: IMsgParams): boolean {
|
package/src/types.ts
CHANGED
package/src/utils/constant.ts
CHANGED
|
@@ -2,6 +2,6 @@ export const ENV: 'production' | 'test' | 'develop' = 'test'
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
export const systemCommand = ['/new', '/status']
|
|
5
|
-
export const interruptCommand = ['
|
|
5
|
+
export const interruptCommand = ['chat.stop']
|
|
6
6
|
|
|
7
|
-
export const ignoreToolCommand = ['/search', '/abort', '/queue interrupt', ...systemCommand, ...interruptCommand]
|
|
7
|
+
export const ignoreToolCommand = ['/search', '/abort', '/stop', '/queue interrupt', ...systemCommand, ...interruptCommand]
|
package/src/utils/global.ts
CHANGED
|
@@ -68,12 +68,22 @@ export function getDcgchatRuntime(): PluginRuntime {
|
|
|
68
68
|
return runtime as PluginRuntime
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
export type MsgSessionStatus = 'running' | 'finished' | ''
|
|
72
|
+
|
|
73
|
+
const msgStatusBySessionKey = new Map<string, MsgSessionStatus>()
|
|
74
|
+
|
|
75
|
+
export function setMsgStatus(sessionKey: string, status: MsgSessionStatus) {
|
|
76
|
+
if (!sessionKey?.trim()) return
|
|
77
|
+
if (status === '') {
|
|
78
|
+
msgStatusBySessionKey.delete(sessionKey)
|
|
79
|
+
} else {
|
|
80
|
+
msgStatusBySessionKey.set(sessionKey, status)
|
|
81
|
+
}
|
|
74
82
|
}
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
|
|
84
|
+
export function getMsgStatus(sessionKey: string): MsgSessionStatus {
|
|
85
|
+
if (!sessionKey?.trim()) return ''
|
|
86
|
+
return msgStatusBySessionKey.get(sessionKey) ?? ''
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
const getMediaKey = (url: string) => url.split(/[\\/]/).pop() ?? url
|
|
@@ -106,3 +116,16 @@ export function clearSentMediaKeys(messageId?: string) {
|
|
|
106
116
|
sentMediaKeysBySession.clear()
|
|
107
117
|
}
|
|
108
118
|
}
|
|
119
|
+
|
|
120
|
+
export const getSessionKey = (content: any, accountId: string) => {
|
|
121
|
+
const { real_mobook, agent_id, conversation_id, session_id } = content
|
|
122
|
+
const core = getDcgchatRuntime()
|
|
123
|
+
|
|
124
|
+
const route = core.channel.routing.resolveAgentRoute({
|
|
125
|
+
cfg: getOpenClawConfig() as OpenClawConfig,
|
|
126
|
+
channel: "dcgchat-test",
|
|
127
|
+
accountId: accountId || 'default',
|
|
128
|
+
peer: { kind: 'direct', id: session_id }
|
|
129
|
+
})
|
|
130
|
+
return real_mobook === '1' ? route.sessionKey : `agent:main:mobook:direct:${agent_id}:${session_id}`.toLowerCase()
|
|
131
|
+
}
|