@eyeclaw/eyeclaw 2.3.11 → 2.3.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/package.json +1 -1
- package/src/websocket-client.ts +43 -0
package/package.json
CHANGED
package/src/websocket-client.ts
CHANGED
|
@@ -27,6 +27,7 @@ export class EyeClawWebSocketClient {
|
|
|
27
27
|
private subscribed = false
|
|
28
28
|
private pingInterval: any = null
|
|
29
29
|
private chunkSequence = 0 // 每个会话的 chunk 序号
|
|
30
|
+
private accumulatedContent = '' // 累积完整内容用于兜底
|
|
30
31
|
|
|
31
32
|
constructor(api: OpenClawPluginApi, config: EyeClawConfig, getState: () => any) {
|
|
32
33
|
this.api = api
|
|
@@ -244,6 +245,9 @@ export class EyeClawWebSocketClient {
|
|
|
244
245
|
if (done) {
|
|
245
246
|
// 流结束,通知 Rails
|
|
246
247
|
this.sendMessage('stream_end', { session_id: sessionId })
|
|
248
|
+
|
|
249
|
+
// 发送 stream_summary 用于兜底机制
|
|
250
|
+
this.sendStreamSummary(sessionId)
|
|
247
251
|
break
|
|
248
252
|
}
|
|
249
253
|
|
|
@@ -322,6 +326,10 @@ export class EyeClawWebSocketClient {
|
|
|
322
326
|
private sendChunk(content: string, sessionId?: string) {
|
|
323
327
|
const timestamp = new Date().toISOString();
|
|
324
328
|
const sequence = this.chunkSequence++;
|
|
329
|
+
|
|
330
|
+
// 累积完整内容用于兜底
|
|
331
|
+
this.accumulatedContent += content;
|
|
332
|
+
|
|
325
333
|
this.api.logger.info(`[EyeClaw] [${timestamp}] Sending chunk #${sequence} to Rails: "${content}"`);
|
|
326
334
|
this.sendMessage('stream_chunk', {
|
|
327
335
|
content,
|
|
@@ -329,6 +337,41 @@ export class EyeClawWebSocketClient {
|
|
|
329
337
|
sequence, // 添加序号
|
|
330
338
|
})
|
|
331
339
|
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 发送 stream_summary 用于兜底机制
|
|
343
|
+
* 告诉 Rails 完整内容是什么,以便检测丢包并补偿
|
|
344
|
+
*/
|
|
345
|
+
private sendStreamSummary(sessionId?: string) {
|
|
346
|
+
// 计算内容 hash
|
|
347
|
+
const contentHash = this.hashCode(this.accumulatedContent);
|
|
348
|
+
|
|
349
|
+
this.api.logger.info(`[EyeClaw] Sending stream_summary: chunks=${this.chunkSequence}, content_len=${this.accumulatedContent.length}, hash=${contentHash}`);
|
|
350
|
+
|
|
351
|
+
this.sendMessage('stream_summary', {
|
|
352
|
+
session_id: sessionId,
|
|
353
|
+
total_content: this.accumulatedContent,
|
|
354
|
+
total_chunks: this.chunkSequence,
|
|
355
|
+
content_hash: contentHash,
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
// 重置累积内容(为下一个会话做准备)
|
|
359
|
+
this.accumulatedContent = '';
|
|
360
|
+
this.chunkSequence = 0;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 简单 hash 函数
|
|
365
|
+
*/
|
|
366
|
+
private hashCode(str: string): string {
|
|
367
|
+
let hash = 0;
|
|
368
|
+
for (let i = 0; i < str.length; i++) {
|
|
369
|
+
const char = str.charCodeAt(i);
|
|
370
|
+
hash = ((hash << 5) - hash) + char;
|
|
371
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
372
|
+
}
|
|
373
|
+
return hash.toString(16);
|
|
374
|
+
}
|
|
332
375
|
|
|
333
376
|
/**
|
|
334
377
|
* 发送消息到 Rails(带 channel identifier)
|