openclaw-xiaoyou 1.3.1 → 1.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/channel.ts +41 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-xiaoyou",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "type": "module",
5
5
  "description": "Xiaoyou channel plugin for OpenClaw — connects enterprise services via persistent outbound WebSocket",
6
6
  "openclaw": {
package/src/channel.ts CHANGED
@@ -16,6 +16,25 @@ let _runtime: any = null;
16
16
  export function setRuntime(rt: any) { _runtime = rt; }
17
17
  export function getRuntime() { return _runtime; }
18
18
 
19
+ // ─── 按标点/换行拆分文本 ─────────────────────────────
20
+
21
+ /**
22
+ * 将文本按中英文标点符号和换行符拆分为多个片段。
23
+ * 每个片段以标点或换行结尾(保留标点在片段内)。
24
+ */
25
+ function splitBySentence(text: string): string[] {
26
+ // 匹配:中文标点(。!?;)、英文标点(.!?;)后跟空格或结尾、换行符
27
+ const parts = text.split(/(?<=[。!?;\n])|(?<=[.!?;]\s)/);
28
+ const result: string[] = [];
29
+ for (const part of parts) {
30
+ const trimmed = part;
31
+ if (trimmed.length > 0) {
32
+ result.push(trimmed);
33
+ }
34
+ }
35
+ return result.length > 0 ? result : [text];
36
+ }
37
+
19
38
  // ─── Config Adapter ──────────────────────────────────
20
39
 
21
40
  function getChannelConfig(cfg: any): any {
@@ -180,15 +199,10 @@ export const xiayouPlugin = {
180
199
  Timestamp: Date.now(),
181
200
  });
182
201
 
183
- // 4. 分发并获取回复(流式 — block 级)
202
+ // 4. 分发并获取回复(流式 — 按标点拆分)
184
203
  //
185
- // OpenClaw 2026.3.x 的 block streaming 机制:
186
- // capabilities.blockStreaming = true 告诉 Gateway 按 block 逐个调用 deliver,
187
- // 而非等全部生成完毕。每个 block(段落/代码块等)生成完成后立即 deliver。
188
- // 统一使用 type="reply",通过 streamStatus 字段区分:
189
- // streamStatus="chunk" — 增量片段
190
- // streamStatus="end" — 流结束(text 为完整文本)
191
- // 无 streamStatus — 一次性完整回复(向后兼容)
204
+ // Gateway 的 block streaming 会多次调用 deliver,每次一个 block。
205
+ // 我们在 deliver 内部进一步按标点/换行拆分,实现更细粒度的流式推送。
192
206
  //
193
207
  const replyMessageId = `xiaoyou-${Date.now()}`;
194
208
  let chunkSeq = 0;
@@ -204,21 +218,25 @@ export const xiayouPlugin = {
204
218
  const textToSend = payload.markdown || payload.text;
205
219
  if (!textToSend) return;
206
220
 
207
- fullText += (fullText ? "\n" : "") + textToSend;
208
-
209
- if (_client && _client.isConnected()) {
210
- _client.sendReply({
211
- type: "reply",
212
- conversationId,
213
- messageId: replyMessageId,
214
- replyToMessageId: inboundMessageId,
215
- text: textToSend,
216
- streamStatus: "chunk",
217
- seq: chunkSeq++,
218
- timestamp: Date.now(),
219
- });
220
- logger.info(`[xiaoyou] chunk #${chunkSeq} sent to ${conversationId}`);
221
+ // 按标点/换行拆分为更细的片段
222
+ const sentences = splitBySentence(textToSend);
223
+
224
+ for (const sentence of sentences) {
225
+ fullText += sentence;
226
+ if (_client && _client.isConnected()) {
227
+ _client.sendReply({
228
+ type: "reply",
229
+ conversationId,
230
+ messageId: replyMessageId,
231
+ replyToMessageId: inboundMessageId,
232
+ text: sentence,
233
+ streamStatus: "chunk",
234
+ seq: chunkSeq++,
235
+ timestamp: Date.now(),
236
+ });
237
+ }
221
238
  }
239
+ logger.info(`[xiaoyou] ${sentences.length} chunks sent to ${conversationId} (total seq=${chunkSeq})`);
222
240
  },
223
241
  onComplete: async () => {
224
242
  replyEndSent = true;
@@ -238,7 +256,7 @@ export const xiayouPlugin = {
238
256
  },
239
257
  });
240
258
 
241
- // dispatch resolve 后,如果 onComplete 未被调用(旧版本兼容),手动发 end
259
+ // dispatch resolve 后,如果 onComplete 未被调用(兼容),手动发 end
242
260
  if (!replyEndSent && chunkSeq > 0 && _client && _client.isConnected()) {
243
261
  _client.sendReply({
244
262
  type: "reply",