openclaw-lark-multi-agent 1.0.10 → 1.0.12
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/dist/openclaw-client.d.ts +10 -0
- package/dist/openclaw-client.js +67 -15
- package/package.json +1 -1
|
@@ -36,6 +36,13 @@ export declare class OpenClawClient {
|
|
|
36
36
|
/** Session keys whose proactive messages must be dropped by the bridge (e.g. discussion scheduler owns delivery). */
|
|
37
37
|
private mutedProactiveSessions;
|
|
38
38
|
private mutedProactiveSessionCounts;
|
|
39
|
+
/** Global limiter for chat.send RPC calls; large multi-bot fan-out can
|
|
40
|
+
* saturate the Gateway before collectReply even starts. The slot is released
|
|
41
|
+
* as soon as the chat.send RPC returns a runId; collectReply does not hold it.
|
|
42
|
+
*/
|
|
43
|
+
private chatSendConcurrency;
|
|
44
|
+
private activeChatSends;
|
|
45
|
+
private chatSendWaiters;
|
|
39
46
|
constructor(config: OpenClawConfig);
|
|
40
47
|
connect(): Promise<void>;
|
|
41
48
|
private _doConnect;
|
|
@@ -52,6 +59,7 @@ export declare class OpenClawClient {
|
|
|
52
59
|
* 30-minute safety net only for catastrophic WS disconnection.
|
|
53
60
|
*/
|
|
54
61
|
private collectReply;
|
|
62
|
+
private pickBestCollectedText;
|
|
55
63
|
createSession(params: {
|
|
56
64
|
key: string;
|
|
57
65
|
model: string;
|
|
@@ -92,6 +100,8 @@ export declare class OpenClawClient {
|
|
|
92
100
|
private addMutedProactiveKey;
|
|
93
101
|
private releaseMutedProactiveKey;
|
|
94
102
|
muteProactiveDelivery(sessionKey: string): (delayMs?: number) => void;
|
|
103
|
+
private acquireChatSendSlot;
|
|
104
|
+
private releaseChatSendSlot;
|
|
95
105
|
chatSend(params: {
|
|
96
106
|
sessionKey: string;
|
|
97
107
|
message: string;
|
package/dist/openclaw-client.js
CHANGED
|
@@ -37,6 +37,13 @@ export class OpenClawClient {
|
|
|
37
37
|
/** Session keys whose proactive messages must be dropped by the bridge (e.g. discussion scheduler owns delivery). */
|
|
38
38
|
mutedProactiveSessions = new Set();
|
|
39
39
|
mutedProactiveSessionCounts = new Map();
|
|
40
|
+
/** Global limiter for chat.send RPC calls; large multi-bot fan-out can
|
|
41
|
+
* saturate the Gateway before collectReply even starts. The slot is released
|
|
42
|
+
* as soon as the chat.send RPC returns a runId; collectReply does not hold it.
|
|
43
|
+
*/
|
|
44
|
+
chatSendConcurrency = Number(process.env.OPENCLAW_LARK_MULTI_AGENT_CHAT_SEND_CONCURRENCY || 3);
|
|
45
|
+
activeChatSends = 0;
|
|
46
|
+
chatSendWaiters = [];
|
|
40
47
|
constructor(config) {
|
|
41
48
|
this.config = config;
|
|
42
49
|
}
|
|
@@ -185,7 +192,7 @@ export class OpenClawClient {
|
|
|
185
192
|
this.agentEvents.get(rawKey).push({
|
|
186
193
|
runId: frame.payload.runId,
|
|
187
194
|
sessionKey: rawKey,
|
|
188
|
-
stream: "
|
|
195
|
+
stream: "transcriptAssistant",
|
|
189
196
|
data: { deltaText: visibleAssistantText, delta: visibleAssistantText, replace: true },
|
|
190
197
|
});
|
|
191
198
|
}
|
|
@@ -329,6 +336,7 @@ export class OpenClawClient {
|
|
|
329
336
|
let text = "";
|
|
330
337
|
let chatDeltaText = "";
|
|
331
338
|
let chatFinalText = "";
|
|
339
|
+
let transcriptAssistantText = "";
|
|
332
340
|
let sessionKey = targetSessionKey ? `agent:main:${targetSessionKey.replace(/^agent:[^:]+:/, "")}` : "";
|
|
333
341
|
let shortSessionKey = targetSessionKey ? targetSessionKey.replace(/^agent:[^:]+:/, "") : "";
|
|
334
342
|
let chatFinalTimer = null;
|
|
@@ -367,7 +375,7 @@ export class OpenClawClient {
|
|
|
367
375
|
this.abortChat(targetSessionKey || sessionKey, runId).catch((err) => {
|
|
368
376
|
console.warn(`[OpenClaw] abort after collectReply idle timeout failed:`, err.message);
|
|
369
377
|
});
|
|
370
|
-
const visibleText = text
|
|
378
|
+
const visibleText = this.pickBestCollectedText(chatFinalText, text, chatDeltaText, transcriptAssistantText);
|
|
371
379
|
if (visibleText) {
|
|
372
380
|
resolve(visibleText);
|
|
373
381
|
}
|
|
@@ -392,7 +400,7 @@ export class OpenClawClient {
|
|
|
392
400
|
}
|
|
393
401
|
return `运行事件 item${data.kind ? `/${data.kind}` : ""}`;
|
|
394
402
|
}
|
|
395
|
-
if (ev.stream === "assistant" || ev.stream === "chatDelta") {
|
|
403
|
+
if (ev.stream === "assistant" || ev.stream === "chatDelta" || ev.stream === "transcriptAssistant") {
|
|
396
404
|
const chunk = String(ev.data?.deltaText || ev.data?.delta || "").trim();
|
|
397
405
|
return chunk ? `模型输出片段: ${chunk.slice(0, 300)}` : "模型输出片段";
|
|
398
406
|
}
|
|
@@ -543,7 +551,7 @@ export class OpenClawClient {
|
|
|
543
551
|
lifecycleStartedLogged = true;
|
|
544
552
|
console.log(`[OpenClaw] lifecycle start for runId=${runId} after ${Date.now() - collectStartedAt}ms`);
|
|
545
553
|
}
|
|
546
|
-
if ((ev.stream === "assistant" || ev.stream === "chatDelta") && (ev.data?.deltaText || ev.data?.delta)) {
|
|
554
|
+
if ((ev.stream === "assistant" || ev.stream === "chatDelta" || ev.stream === "transcriptAssistant") && (ev.data?.deltaText || ev.data?.delta)) {
|
|
547
555
|
const chunk = ev.data.deltaText || ev.data.delta;
|
|
548
556
|
if (ev.stream === "assistant") {
|
|
549
557
|
if (ev.data?.replace) {
|
|
@@ -553,7 +561,7 @@ export class OpenClawClient {
|
|
|
553
561
|
text += chunk;
|
|
554
562
|
}
|
|
555
563
|
}
|
|
556
|
-
else {
|
|
564
|
+
else if (ev.stream === "chatDelta") {
|
|
557
565
|
if (ev.data?.replace) {
|
|
558
566
|
chatDeltaText = chunk;
|
|
559
567
|
}
|
|
@@ -561,6 +569,14 @@ export class OpenClawClient {
|
|
|
561
569
|
chatDeltaText += chunk;
|
|
562
570
|
}
|
|
563
571
|
}
|
|
572
|
+
else {
|
|
573
|
+
if (ev.data?.replace) {
|
|
574
|
+
transcriptAssistantText = chunk;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
transcriptAssistantText += chunk;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
564
580
|
}
|
|
565
581
|
if (ev.stream === "chatFinal") {
|
|
566
582
|
chatFinalText = ev.data?.text || "";
|
|
@@ -573,7 +589,7 @@ export class OpenClawClient {
|
|
|
573
589
|
});
|
|
574
590
|
// Prefer final chat message over accumulated deltas: some providers may
|
|
575
591
|
// emit only partial deltas (e.g. "N") while final contains "NO_REPLY".
|
|
576
|
-
const latestFinalText = chatFinalText
|
|
592
|
+
const latestFinalText = this.pickBestCollectedText(chatFinalText, text, chatDeltaText, transcriptAssistantText);
|
|
577
593
|
if (latestFinalText) {
|
|
578
594
|
finish(latestFinalText);
|
|
579
595
|
}
|
|
@@ -590,9 +606,9 @@ export class OpenClawClient {
|
|
|
590
606
|
if (ev.stream === "lifecycle" && ev.data?.phase === "end") {
|
|
591
607
|
// Prefer final chat message over accumulated deltas: some providers may
|
|
592
608
|
// emit only partial deltas (e.g. "N") while final contains "NO_REPLY".
|
|
593
|
-
const finalText = chatFinalText
|
|
609
|
+
const finalText = this.pickBestCollectedText(chatFinalText, text, chatDeltaText, transcriptAssistantText);
|
|
594
610
|
const finishFromLifecycle = () => {
|
|
595
|
-
const latestFinalText = chatFinalText
|
|
611
|
+
const latestFinalText = this.pickBestCollectedText(chatFinalText, text, chatDeltaText, transcriptAssistantText);
|
|
596
612
|
if (!chatFinalText && latestFinalText.trim() === "N") {
|
|
597
613
|
// Some providers stream the first character of NO_REPLY ("N") but
|
|
598
614
|
// never deliver a final chat message in time. Never surface a lone
|
|
@@ -655,6 +671,17 @@ export class OpenClawClient {
|
|
|
655
671
|
}, 50);
|
|
656
672
|
});
|
|
657
673
|
}
|
|
674
|
+
pickBestCollectedText(chatFinalText, assistantText, chatDeltaText, transcriptAssistantText) {
|
|
675
|
+
if (chatFinalText)
|
|
676
|
+
return chatFinalText;
|
|
677
|
+
const candidates = [assistantText, chatDeltaText, transcriptAssistantText].filter((value) => value && value.trim());
|
|
678
|
+
if (candidates.length === 0)
|
|
679
|
+
return "";
|
|
680
|
+
// transcriptAssistant is a full transcript mirror. Keep it separate from
|
|
681
|
+
// streaming deltas to avoid concatenating the same response twice; choose
|
|
682
|
+
// the richest available non-final text as fallback.
|
|
683
|
+
return candidates.sort((a, b) => b.length - a.length)[0];
|
|
684
|
+
}
|
|
658
685
|
// --- Session management ---
|
|
659
686
|
async createSession(params) {
|
|
660
687
|
return this.rpc("sessions.create", params);
|
|
@@ -837,6 +864,24 @@ export class OpenClawClient {
|
|
|
837
864
|
release();
|
|
838
865
|
};
|
|
839
866
|
}
|
|
867
|
+
async acquireChatSendSlot() {
|
|
868
|
+
const limit = Math.max(1, this.chatSendConcurrency || 1);
|
|
869
|
+
if (this.activeChatSends < limit) {
|
|
870
|
+
this.activeChatSends++;
|
|
871
|
+
return () => this.releaseChatSendSlot();
|
|
872
|
+
}
|
|
873
|
+
const startedWaitingAt = Date.now();
|
|
874
|
+
await new Promise((resolve) => this.chatSendWaiters.push(resolve));
|
|
875
|
+
this.activeChatSends++;
|
|
876
|
+
console.log(`[OpenClaw] chat.send waited ${Date.now() - startedWaitingAt}ms for concurrency slot (active=${this.activeChatSends}/${limit})`);
|
|
877
|
+
return () => this.releaseChatSendSlot();
|
|
878
|
+
}
|
|
879
|
+
releaseChatSendSlot() {
|
|
880
|
+
this.activeChatSends = Math.max(0, this.activeChatSends - 1);
|
|
881
|
+
const next = this.chatSendWaiters.shift();
|
|
882
|
+
if (next)
|
|
883
|
+
next();
|
|
884
|
+
}
|
|
840
885
|
async chatSend(params) {
|
|
841
886
|
const sk = params.sessionKey;
|
|
842
887
|
const fullSessionKey = `agent:main:${sk}`;
|
|
@@ -849,14 +894,21 @@ export class OpenClawClient {
|
|
|
849
894
|
// the next message while still allowing sessionKey matching for internal runIds.
|
|
850
895
|
this.agentEvents.set(fullSessionKey, []);
|
|
851
896
|
this.agentEvents.set(sk, []);
|
|
897
|
+
const releaseChatSendSlot = await this.acquireChatSendSlot();
|
|
898
|
+
let result;
|
|
852
899
|
const sendStartedAt = Date.now();
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
900
|
+
try {
|
|
901
|
+
result = await this.rpc("chat.send", {
|
|
902
|
+
sessionKey: sk,
|
|
903
|
+
message: params.message,
|
|
904
|
+
attachments: params.attachments,
|
|
905
|
+
deliver: params.deliver ?? false,
|
|
906
|
+
idempotencyKey: randomUUID(),
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
finally {
|
|
910
|
+
releaseChatSendSlot();
|
|
911
|
+
}
|
|
860
912
|
console.log(`[OpenClaw] chat.send runId: ${result.runId} (rpc=${Date.now() - sendStartedAt}ms, attachments=${params.attachments?.length || 0})`);
|
|
861
913
|
return await this.collectReply(result.runId, params.timeoutMs || 1800000, sk, { emptyFinalAsNoReply: params.emptyFinalAsNoReply, expectedUserText: params.message });
|
|
862
914
|
}
|