@capyclaw/tool-host 0.1.0 → 0.1.1
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/dsh-suite.json +6 -4
- package/package.json +15 -2
- package/src/index.mjs +341 -341
- package/src/optimizers/E2E/346/265/213/350/257/225/346/212/245/345/221/212.md +0 -396
- package/src/optimizers/PPT/345/234/272/346/231/257/344/274/230/345/214/226/346/226/271/346/241/210.md +0 -607
- package/src/optimizers/PPT/345/256/236/346/227/266/346/270/262/346/237/223/346/226/271/346/241/210.md +0 -456
- package/src/optimizers/PPT/346/265/201/345/274/217/346/270/262/346/237/223-/347/224/237/344/272/247/347/272/247/344/274/230/345/214/226.md +0 -874
- package/src/optimizers/README.md +0 -374
- package/src/optimizers/batch-optimizer.mjs +0 -94
- package/src/optimizers/cache-optimizer.mjs +0 -217
- package/src/optimizers/concurrency-optimizer.mjs +0 -129
- package/src/optimizers/e2e-test.mjs +0 -674
- package/src/optimizers/examples.mjs +0 -243
- package/src/optimizers/index.mjs +0 -188
- package/src/optimizers/ppt-optimizer.mjs +0 -263
- package/src/optimizers/ppt-streaming-demo.mjs +0 -192
- package/src/optimizers/ppt-streaming-renderer.mjs +0 -332
- package/src/optimizers/production-streaming-demo.mjs +0 -267
- package/src/optimizers/production-streaming-renderer.mjs +0 -611
- package/src/optimizers/real-ppt-test.mjs +0 -397
- package/src/optimizers/real-world-benchmark.mjs +0 -470
- package/src/optimizers/retry-optimizer.mjs +0 -118
- package/src/optimizers//347/224/237/344/272/247/347/272/247/344/274/230/345/214/226/346/200/273/347/273/223.md +0 -110
- package/src/optimizers//347/234/237/345/256/236/346/200/247/350/203/275/346/217/220/345/215/207/351/252/214/350/257/201/346/212/245/345/221/212.md +0 -331
|
@@ -1,874 +0,0 @@
|
|
|
1
|
-
# 🚀 PPT 流式渲染 - 生产级优化方案
|
|
2
|
-
|
|
3
|
-
**目标**: 确保传输稳定性、低延迟、低性能开销和高准确性
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 📋 核心优化策略
|
|
8
|
-
|
|
9
|
-
### 参考:成熟的流式输出实践
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
LLM 流式输出的成熟技术:
|
|
13
|
-
✅ Server-Sent Events (SSE) - 单向流式传输
|
|
14
|
-
✅ 断点续传 - 支持中断恢复
|
|
15
|
-
✅ 心跳检测 - 检测连接状态
|
|
16
|
-
✅ 缓冲区管理 - 平滑输出
|
|
17
|
-
✅ 错误重试 - 自动恢复
|
|
18
|
-
✅ 优雅降级 - 回退到轮询
|
|
19
|
-
✅ 反压控制 - 防止过载
|
|
20
|
-
|
|
21
|
-
我们可以借鉴所有这些技术!
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 🎯 优化维度
|
|
27
|
-
|
|
28
|
-
### 1. 稳定性优化
|
|
29
|
-
|
|
30
|
-
#### 1.1 连接管理
|
|
31
|
-
|
|
32
|
-
```javascript
|
|
33
|
-
class StableConnection {
|
|
34
|
-
constructor(wsUrl) {
|
|
35
|
-
this.wsUrl = wsUrl;
|
|
36
|
-
this.ws = null;
|
|
37
|
-
this.reconnectAttempts = 0;
|
|
38
|
-
this.maxReconnectAttempts = 5;
|
|
39
|
-
this.reconnectDelay = 1000;
|
|
40
|
-
this.heartbeatInterval = null;
|
|
41
|
-
this.lastHeartbeat = Date.now();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async connect() {
|
|
45
|
-
return new Promise((resolve, reject) => {
|
|
46
|
-
this.ws = new WebSocket(this.wsUrl);
|
|
47
|
-
|
|
48
|
-
this.ws.onopen = () => {
|
|
49
|
-
console.log('✅ WebSocket 连接成功');
|
|
50
|
-
this.reconnectAttempts = 0;
|
|
51
|
-
this.startHeartbeat();
|
|
52
|
-
resolve();
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
this.ws.onerror = (error) => {
|
|
56
|
-
console.error('❌ WebSocket 错误:', error);
|
|
57
|
-
this.handleError(error);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
this.ws.onclose = () => {
|
|
61
|
-
console.log('🔌 WebSocket 断开');
|
|
62
|
-
this.stopHeartbeat();
|
|
63
|
-
this.attemptReconnect();
|
|
64
|
-
};
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 心跳检测
|
|
69
|
-
startHeartbeat() {
|
|
70
|
-
this.heartbeatInterval = setInterval(() => {
|
|
71
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
72
|
-
this.ws.send(JSON.stringify({ type: 'ping' }));
|
|
73
|
-
this.lastHeartbeat = Date.now();
|
|
74
|
-
} else {
|
|
75
|
-
// 连接异常,尝试重连
|
|
76
|
-
this.attemptReconnect();
|
|
77
|
-
}
|
|
78
|
-
}, 5000); // 每5秒心跳
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
stopHeartbeat() {
|
|
82
|
-
if (this.heartbeatInterval) {
|
|
83
|
-
clearInterval(this.heartbeatInterval);
|
|
84
|
-
this.heartbeatInterval = null;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 自动重连
|
|
89
|
-
async attemptReconnect() {
|
|
90
|
-
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
91
|
-
console.error('❌ 达到最大重连次数,放弃重连');
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
this.reconnectAttempts++;
|
|
96
|
-
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
97
|
-
|
|
98
|
-
console.log(`🔄 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts}),延迟 ${delay}ms`);
|
|
99
|
-
|
|
100
|
-
await new Promise(r => setTimeout(r, delay));
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
await this.connect();
|
|
104
|
-
console.log('✅ 重连成功');
|
|
105
|
-
} catch (error) {
|
|
106
|
-
console.error('❌ 重连失败:', error);
|
|
107
|
-
this.attemptReconnect();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 安全发送
|
|
112
|
-
safeSend(data) {
|
|
113
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
114
|
-
this.ws.send(JSON.stringify(data));
|
|
115
|
-
return true;
|
|
116
|
-
} else {
|
|
117
|
-
console.warn('⚠️ WebSocket 未连接,消息将被缓存');
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
#### 1.2 消息确认机制(ACK)
|
|
125
|
-
|
|
126
|
-
```javascript
|
|
127
|
-
class ReliableMessageQueue {
|
|
128
|
-
constructor(connection) {
|
|
129
|
-
this.connection = connection;
|
|
130
|
-
this.pendingMessages = new Map(); // 待确认消息
|
|
131
|
-
this.messageId = 0;
|
|
132
|
-
this.ackTimeout = 5000; // 5秒超时
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async sendWithAck(message) {
|
|
136
|
-
const id = ++this.messageId;
|
|
137
|
-
const payload = { ...message, messageId: id };
|
|
138
|
-
|
|
139
|
-
return new Promise((resolve, reject) => {
|
|
140
|
-
// 发送消息
|
|
141
|
-
const sent = this.connection.safeSend(payload);
|
|
142
|
-
|
|
143
|
-
if (!sent) {
|
|
144
|
-
reject(new Error('Connection not ready'));
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 等待确认
|
|
149
|
-
const timeout = setTimeout(() => {
|
|
150
|
-
this.pendingMessages.delete(id);
|
|
151
|
-
reject(new Error(`Message ${id} timeout`));
|
|
152
|
-
}, this.ackTimeout);
|
|
153
|
-
|
|
154
|
-
this.pendingMessages.set(id, {
|
|
155
|
-
resolve,
|
|
156
|
-
reject,
|
|
157
|
-
timeout,
|
|
158
|
-
message: payload,
|
|
159
|
-
timestamp: Date.now()
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
handleAck(messageId) {
|
|
165
|
-
const pending = this.pendingMessages.get(messageId);
|
|
166
|
-
if (pending) {
|
|
167
|
-
clearTimeout(pending.timeout);
|
|
168
|
-
pending.resolve();
|
|
169
|
-
this.pendingMessages.delete(messageId);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// 重发未确认的消息
|
|
174
|
-
retryPending() {
|
|
175
|
-
const now = Date.now();
|
|
176
|
-
for (const [id, pending] of this.pendingMessages.entries()) {
|
|
177
|
-
if (now - pending.timestamp > this.ackTimeout) {
|
|
178
|
-
console.log(`🔄 重发消息 ${id}`);
|
|
179
|
-
this.connection.safeSend(pending.message);
|
|
180
|
-
pending.timestamp = now;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### 2. 断点续传机制
|
|
188
|
-
|
|
189
|
-
#### 2.1 状态持久化
|
|
190
|
-
|
|
191
|
-
```javascript
|
|
192
|
-
class StreamingStateManager {
|
|
193
|
-
constructor() {
|
|
194
|
-
this.sessionId = this.generateSessionId();
|
|
195
|
-
this.currentIndex = 0;
|
|
196
|
-
this.completedOps = new Set();
|
|
197
|
-
this.failedOps = new Set();
|
|
198
|
-
this.snapshot = null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
generateSessionId() {
|
|
202
|
-
return `ppt_stream_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// 保存检查点
|
|
206
|
-
saveCheckpoint(index, operation) {
|
|
207
|
-
this.currentIndex = index;
|
|
208
|
-
this.completedOps.add(index);
|
|
209
|
-
|
|
210
|
-
// 持久化到本地存储
|
|
211
|
-
const state = {
|
|
212
|
-
sessionId: this.sessionId,
|
|
213
|
-
currentIndex: this.currentIndex,
|
|
214
|
-
completedOps: Array.from(this.completedOps),
|
|
215
|
-
failedOps: Array.from(this.failedOps),
|
|
216
|
-
timestamp: Date.now()
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
localStorage.setItem(`ppt_stream_${this.sessionId}`, JSON.stringify(state));
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// 恢复状态
|
|
223
|
-
restoreState(sessionId) {
|
|
224
|
-
const stored = localStorage.getItem(`ppt_stream_${sessionId}`);
|
|
225
|
-
if (stored) {
|
|
226
|
-
const state = JSON.parse(stored);
|
|
227
|
-
this.sessionId = state.sessionId;
|
|
228
|
-
this.currentIndex = state.currentIndex;
|
|
229
|
-
this.completedOps = new Set(state.completedOps);
|
|
230
|
-
this.failedOps = new Set(state.failedOps);
|
|
231
|
-
return state;
|
|
232
|
-
}
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// 检查操作是否已完成
|
|
237
|
-
isCompleted(index) {
|
|
238
|
-
return this.completedOps.has(index);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// 清理状态
|
|
242
|
-
clearState() {
|
|
243
|
-
localStorage.removeItem(`ppt_stream_${this.sessionId}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
#### 2.2 断点续传实现
|
|
249
|
-
|
|
250
|
-
```javascript
|
|
251
|
-
async streamEditWithResume(operations, onProgress, resumeSessionId = null) {
|
|
252
|
-
const stateManager = new StreamingStateManager();
|
|
253
|
-
|
|
254
|
-
// 尝试恢复之前的会话
|
|
255
|
-
if (resumeSessionId) {
|
|
256
|
-
const restored = stateManager.restoreState(resumeSessionId);
|
|
257
|
-
if (restored) {
|
|
258
|
-
console.log(`🔄 恢复会话: ${resumeSessionId},从第 ${restored.currentIndex} 个操作继续`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const total = operations.length;
|
|
263
|
-
|
|
264
|
-
for (let i = 0; i < operations.length; i++) {
|
|
265
|
-
// 跳过已完成的操作
|
|
266
|
-
if (stateManager.isCompleted(i)) {
|
|
267
|
-
console.log(`⏭️ 跳过已完成的操作 ${i}`);
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const operation = operations[i];
|
|
272
|
-
|
|
273
|
-
try {
|
|
274
|
-
// 执行操作
|
|
275
|
-
await this.executeOperation(operation);
|
|
276
|
-
|
|
277
|
-
// 保存检查点
|
|
278
|
-
stateManager.saveCheckpoint(i, operation);
|
|
279
|
-
|
|
280
|
-
// 反馈进度
|
|
281
|
-
if (onProgress) {
|
|
282
|
-
onProgress({
|
|
283
|
-
current: i + 1,
|
|
284
|
-
total,
|
|
285
|
-
completed: stateManager.completedOps.size,
|
|
286
|
-
sessionId: stateManager.sessionId
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
} catch (error) {
|
|
290
|
-
console.error(`❌ 操作 ${i} 失败:`, error);
|
|
291
|
-
stateManager.failedOps.add(i);
|
|
292
|
-
|
|
293
|
-
// 根据错误类型决定是否继续
|
|
294
|
-
if (this.isFatalError(error)) {
|
|
295
|
-
throw error; // 致命错误,中止
|
|
296
|
-
}
|
|
297
|
-
// 否则继续下一个操作
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// 完成后清理状态
|
|
302
|
-
stateManager.clearState();
|
|
303
|
-
|
|
304
|
-
return {
|
|
305
|
-
success: true,
|
|
306
|
-
sessionId: stateManager.sessionId,
|
|
307
|
-
completed: stateManager.completedOps.size,
|
|
308
|
-
failed: stateManager.failedOps.size
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### 3. 性能开销优化
|
|
314
|
-
|
|
315
|
-
#### 3.1 缓冲区管理
|
|
316
|
-
|
|
317
|
-
```javascript
|
|
318
|
-
class StreamBuffer {
|
|
319
|
-
constructor(options = {}) {
|
|
320
|
-
this.maxSize = options.maxSize || 10;
|
|
321
|
-
this.flushInterval = options.flushInterval || 100;
|
|
322
|
-
this.buffer = [];
|
|
323
|
-
this.flushTimer = null;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
add(operation) {
|
|
327
|
-
this.buffer.push(operation);
|
|
328
|
-
|
|
329
|
-
// 达到最大缓冲区大小,立即刷新
|
|
330
|
-
if (this.buffer.length >= this.maxSize) {
|
|
331
|
-
this.flush();
|
|
332
|
-
} else {
|
|
333
|
-
// 否则定时刷新
|
|
334
|
-
this.scheduleFlush();
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
scheduleFlush() {
|
|
339
|
-
if (this.flushTimer) return;
|
|
340
|
-
|
|
341
|
-
this.flushTimer = setTimeout(() => {
|
|
342
|
-
this.flush();
|
|
343
|
-
}, this.flushInterval);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
flush() {
|
|
347
|
-
if (this.flushTimer) {
|
|
348
|
-
clearTimeout(this.flushTimer);
|
|
349
|
-
this.flushTimer = null;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (this.buffer.length === 0) return;
|
|
353
|
-
|
|
354
|
-
const batch = this.buffer.splice(0);
|
|
355
|
-
|
|
356
|
-
// 批量发送
|
|
357
|
-
this.sendBatch(batch);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
sendBatch(batch) {
|
|
361
|
-
// 批量发送到前端
|
|
362
|
-
this.connection.send({
|
|
363
|
-
type: 'batch_update',
|
|
364
|
-
operations: batch,
|
|
365
|
-
count: batch.length
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
#### 3.2 反压控制
|
|
372
|
-
|
|
373
|
-
```javascript
|
|
374
|
-
class BackpressureController {
|
|
375
|
-
constructor(options = {}) {
|
|
376
|
-
this.maxInFlight = options.maxInFlight || 5;
|
|
377
|
-
this.inFlight = 0;
|
|
378
|
-
this.queue = [];
|
|
379
|
-
this.paused = false;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
async execute(fn) {
|
|
383
|
-
// 等待空位
|
|
384
|
-
while (this.inFlight >= this.maxInFlight || this.paused) {
|
|
385
|
-
await new Promise(resolve => {
|
|
386
|
-
this.queue.push(resolve);
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
this.inFlight++;
|
|
391
|
-
|
|
392
|
-
try {
|
|
393
|
-
return await fn();
|
|
394
|
-
} finally {
|
|
395
|
-
this.inFlight--;
|
|
396
|
-
this.resume();
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
pause() {
|
|
401
|
-
this.paused = true;
|
|
402
|
-
console.log('⏸️ 暂停流式输出');
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
resume() {
|
|
406
|
-
this.paused = false;
|
|
407
|
-
|
|
408
|
-
// 唤醒等待的任务
|
|
409
|
-
while (this.queue.length > 0 && this.inFlight < this.maxInFlight) {
|
|
410
|
-
const resolve = this.queue.shift();
|
|
411
|
-
resolve();
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
getStatus() {
|
|
416
|
-
return {
|
|
417
|
-
inFlight: this.inFlight,
|
|
418
|
-
queued: this.queue.length,
|
|
419
|
-
paused: this.paused
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### 4. 延迟优化
|
|
426
|
-
|
|
427
|
-
#### 4.1 预测性渲染
|
|
428
|
-
|
|
429
|
-
```javascript
|
|
430
|
-
class PredictiveRenderer {
|
|
431
|
-
constructor() {
|
|
432
|
-
this.predictions = new Map();
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// 预测下一个操作
|
|
436
|
-
predict(currentOp, remainingOps) {
|
|
437
|
-
// 简单预测:相似操作可能连续
|
|
438
|
-
const nextOp = remainingOps[0];
|
|
439
|
-
if (!nextOp) return null;
|
|
440
|
-
|
|
441
|
-
if (this.isSimilar(currentOp, nextOp)) {
|
|
442
|
-
return nextOp;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
isSimilar(op1, op2) {
|
|
449
|
-
return op1.type === op2.type;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// 预渲染
|
|
453
|
-
async preRender(operation) {
|
|
454
|
-
console.log(`🔮 预渲染: ${operation.type}`);
|
|
455
|
-
|
|
456
|
-
// 提前准备资源
|
|
457
|
-
if (operation.type === 'ppt_add_image') {
|
|
458
|
-
// 预加载图片
|
|
459
|
-
await this.preloadImage(operation.data.url);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
async preloadImage(url) {
|
|
464
|
-
return new Promise((resolve, reject) => {
|
|
465
|
-
const img = new Image();
|
|
466
|
-
img.onload = () => resolve(img);
|
|
467
|
-
img.onerror = reject;
|
|
468
|
-
img.src = url;
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
#### 4.2 增量更新
|
|
475
|
-
|
|
476
|
-
```javascript
|
|
477
|
-
class IncrementalUpdater {
|
|
478
|
-
constructor() {
|
|
479
|
-
this.lastSnapshot = null;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// 只发送变化的部分
|
|
483
|
-
calculateDiff(newState, oldState) {
|
|
484
|
-
if (!oldState) return newState;
|
|
485
|
-
|
|
486
|
-
const diff = {};
|
|
487
|
-
for (const key in newState) {
|
|
488
|
-
if (JSON.stringify(newState[key]) !== JSON.stringify(oldState[key])) {
|
|
489
|
-
diff[key] = newState[key];
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return diff;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
send(connection, newState) {
|
|
497
|
-
const diff = this.calculateDiff(newState, this.lastSnapshot);
|
|
498
|
-
|
|
499
|
-
if (Object.keys(diff).length > 0) {
|
|
500
|
-
connection.send({
|
|
501
|
-
type: 'incremental_update',
|
|
502
|
-
diff: diff,
|
|
503
|
-
timestamp: Date.now()
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
this.lastSnapshot = { ...newState };
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
### 5. 准确性保障
|
|
513
|
-
|
|
514
|
-
#### 5.1 操作验证
|
|
515
|
-
|
|
516
|
-
```javascript
|
|
517
|
-
class OperationValidator {
|
|
518
|
-
validate(operation) {
|
|
519
|
-
// 1. 类型检查
|
|
520
|
-
if (!operation.type || typeof operation.type !== 'string') {
|
|
521
|
-
throw new Error('Invalid operation type');
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// 2. 数据完整性
|
|
525
|
-
if (!operation.data) {
|
|
526
|
-
throw new Error('Missing operation data');
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// 3. 依赖检查
|
|
530
|
-
if (operation.dependencies) {
|
|
531
|
-
for (const dep of operation.dependencies) {
|
|
532
|
-
if (!this.isOperationCompleted(dep)) {
|
|
533
|
-
throw new Error(`Dependency not met: ${dep}`);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// 4. 冲突检查
|
|
539
|
-
if (this.hasConflict(operation)) {
|
|
540
|
-
throw new Error('Operation conflicts with existing state');
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
hasConflict(operation) {
|
|
547
|
-
// 检查是否与当前状态冲突
|
|
548
|
-
// 例如:尝试删除不存在的元素
|
|
549
|
-
return false;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
#### 5.2 事务性执行
|
|
555
|
-
|
|
556
|
-
```javascript
|
|
557
|
-
class TransactionalExecutor {
|
|
558
|
-
constructor() {
|
|
559
|
-
this.transactions = [];
|
|
560
|
-
this.currentTransaction = null;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
async beginTransaction() {
|
|
564
|
-
this.currentTransaction = {
|
|
565
|
-
id: Date.now(),
|
|
566
|
-
operations: [],
|
|
567
|
-
snapshots: [],
|
|
568
|
-
status: 'pending'
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
async execute(operation) {
|
|
573
|
-
// 保存快照
|
|
574
|
-
const snapshot = await this.captureSnapshot();
|
|
575
|
-
this.currentTransaction.snapshots.push(snapshot);
|
|
576
|
-
|
|
577
|
-
try {
|
|
578
|
-
// 执行操作
|
|
579
|
-
const result = await this.doExecute(operation);
|
|
580
|
-
this.currentTransaction.operations.push({
|
|
581
|
-
operation,
|
|
582
|
-
result,
|
|
583
|
-
status: 'success'
|
|
584
|
-
});
|
|
585
|
-
return result;
|
|
586
|
-
} catch (error) {
|
|
587
|
-
// 执行失败,回滚
|
|
588
|
-
this.currentTransaction.operations.push({
|
|
589
|
-
operation,
|
|
590
|
-
error,
|
|
591
|
-
status: 'failed'
|
|
592
|
-
});
|
|
593
|
-
throw error;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
async commit() {
|
|
598
|
-
this.currentTransaction.status = 'committed';
|
|
599
|
-
this.transactions.push(this.currentTransaction);
|
|
600
|
-
this.currentTransaction = null;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
async rollback() {
|
|
604
|
-
console.log('🔄 回滚事务');
|
|
605
|
-
|
|
606
|
-
// 恢复到最近的快照
|
|
607
|
-
const lastSnapshot = this.currentTransaction.snapshots[0];
|
|
608
|
-
await this.restoreSnapshot(lastSnapshot);
|
|
609
|
-
|
|
610
|
-
this.currentTransaction.status = 'rolled_back';
|
|
611
|
-
this.currentTransaction = null;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
### 6. 中断与恢复
|
|
617
|
-
|
|
618
|
-
#### 6.1 优雅中断
|
|
619
|
-
|
|
620
|
-
```javascript
|
|
621
|
-
class GracefulInterrupter {
|
|
622
|
-
constructor() {
|
|
623
|
-
this.interrupted = false;
|
|
624
|
-
this.currentOperation = null;
|
|
625
|
-
this.completedOperations = [];
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// 请求中断
|
|
629
|
-
interrupt() {
|
|
630
|
-
console.log('⚠️ 收到中断请求');
|
|
631
|
-
this.interrupted = true;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// 检查是否应该中断
|
|
635
|
-
shouldInterrupt() {
|
|
636
|
-
return this.interrupted;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// 优雅收尾
|
|
640
|
-
async gracefulShutdown(onProgress) {
|
|
641
|
-
console.log('🔚 开始优雅收尾...');
|
|
642
|
-
|
|
643
|
-
// 1. 完成当前操作
|
|
644
|
-
if (this.currentOperation) {
|
|
645
|
-
console.log(' ⏳ 等待当前操作完成...');
|
|
646
|
-
await this.currentOperation;
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// 2. 保存状态
|
|
650
|
-
console.log(' 💾 保存进度...');
|
|
651
|
-
await this.saveProgress();
|
|
652
|
-
|
|
653
|
-
// 3. 通知前端
|
|
654
|
-
if (onProgress) {
|
|
655
|
-
onProgress({
|
|
656
|
-
status: 'interrupted',
|
|
657
|
-
completed: this.completedOperations.length,
|
|
658
|
-
message: '操作已中断,进度已保存'
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
console.log('✅ 优雅收尾完成');
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// 保存进度
|
|
666
|
-
async saveProgress() {
|
|
667
|
-
const state = {
|
|
668
|
-
completed: this.completedOperations,
|
|
669
|
-
timestamp: Date.now()
|
|
670
|
-
};
|
|
671
|
-
|
|
672
|
-
localStorage.setItem('interrupted_progress', JSON.stringify(state));
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// 恢复进度
|
|
676
|
-
async restoreProgress() {
|
|
677
|
-
const stored = localStorage.getItem('interrupted_progress');
|
|
678
|
-
if (stored) {
|
|
679
|
-
const state = JSON.parse(stored);
|
|
680
|
-
this.completedOperations = state.completed;
|
|
681
|
-
console.log(`🔄 恢复进度: ${this.completedOperations.length} 个已完成操作`);
|
|
682
|
-
return state;
|
|
683
|
-
}
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
#### 6.2 方向切换处理
|
|
690
|
-
|
|
691
|
-
```javascript
|
|
692
|
-
class DirectionSwitcher {
|
|
693
|
-
constructor() {
|
|
694
|
-
this.currentDirection = null;
|
|
695
|
-
this.pendingSwitch = null;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// 请求切换方向
|
|
699
|
-
async requestSwitch(newDirection) {
|
|
700
|
-
console.log(`🔄 请求切换方向: ${this.currentDirection} → ${newDirection}`);
|
|
701
|
-
|
|
702
|
-
this.pendingSwitch = newDirection;
|
|
703
|
-
|
|
704
|
-
// 1. 等待当前批次完成
|
|
705
|
-
await this.waitForCurrentBatch();
|
|
706
|
-
|
|
707
|
-
// 2. 清理旧方向的资源
|
|
708
|
-
await this.cleanup(this.currentDirection);
|
|
709
|
-
|
|
710
|
-
// 3. 初始化新方向
|
|
711
|
-
await this.initialize(newDirection);
|
|
712
|
-
|
|
713
|
-
this.currentDirection = newDirection;
|
|
714
|
-
this.pendingSwitch = null;
|
|
715
|
-
|
|
716
|
-
console.log(`✅ 方向切换完成: ${newDirection}`);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
async waitForCurrentBatch() {
|
|
720
|
-
// 等待当前批次的所有操作完成
|
|
721
|
-
console.log(' ⏳ 等待当前批次完成...');
|
|
722
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
async cleanup(direction) {
|
|
726
|
-
console.log(` 🧹 清理 ${direction} 的资源...`);
|
|
727
|
-
// 清理未完成的操作
|
|
728
|
-
// 回滚部分更改
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
async initialize(direction) {
|
|
732
|
-
console.log(` 🚀 初始化 ${direction}...`);
|
|
733
|
-
// 准备新方向的资源
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
---
|
|
739
|
-
|
|
740
|
-
## 📊 整合优化器
|
|
741
|
-
|
|
742
|
-
```javascript
|
|
743
|
-
/**
|
|
744
|
-
* 生产级流式渲染器
|
|
745
|
-
* 集成所有优化策略
|
|
746
|
-
*/
|
|
747
|
-
export class ProductionStreamingRenderer {
|
|
748
|
-
constructor(options = {}) {
|
|
749
|
-
// 连接管理
|
|
750
|
-
this.connection = new StableConnection(options.wsUrl);
|
|
751
|
-
this.messageQueue = new ReliableMessageQueue(this.connection);
|
|
752
|
-
|
|
753
|
-
// 状态管理
|
|
754
|
-
this.stateManager = new StreamingStateManager();
|
|
755
|
-
this.validator = new OperationValidator();
|
|
756
|
-
this.executor = new TransactionalExecutor();
|
|
757
|
-
|
|
758
|
-
// 性能优化
|
|
759
|
-
this.buffer = new StreamBuffer(options.buffer);
|
|
760
|
-
this.backpressure = new BackpressureController(options.backpressure);
|
|
761
|
-
this.predictor = new PredictiveRenderer();
|
|
762
|
-
this.updater = new IncrementalUpdater();
|
|
763
|
-
|
|
764
|
-
// 中断处理
|
|
765
|
-
this.interrupter = new GracefulInterrupter();
|
|
766
|
-
this.switcher = new DirectionSwitcher();
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
async streamEdit(operations, onProgress, options = {}) {
|
|
770
|
-
// 连接
|
|
771
|
-
if (!this.connection.ws) {
|
|
772
|
-
await this.connection.connect();
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
// 恢复会话(如果有)
|
|
776
|
-
if (options.resumeSessionId) {
|
|
777
|
-
this.stateManager.restoreState(options.resumeSessionId);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// 开始事务
|
|
781
|
-
await this.executor.beginTransaction();
|
|
782
|
-
|
|
783
|
-
try {
|
|
784
|
-
for (let i = 0; i < operations.length; i++) {
|
|
785
|
-
// 检查中断
|
|
786
|
-
if (this.interrupter.shouldInterrupt()) {
|
|
787
|
-
await this.interrupter.gracefulShutdown(onProgress);
|
|
788
|
-
break;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
// 检查方向切换
|
|
792
|
-
if (this.switcher.pendingSwitch) {
|
|
793
|
-
await this.switcher.requestSwitch(this.switcher.pendingSwitch);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
const operation = operations[i];
|
|
797
|
-
|
|
798
|
-
// 跳过已完成的
|
|
799
|
-
if (this.stateManager.isCompleted(i)) continue;
|
|
800
|
-
|
|
801
|
-
// 验证操作
|
|
802
|
-
this.validator.validate(operation);
|
|
803
|
-
|
|
804
|
-
// 反压控制
|
|
805
|
-
await this.backpressure.execute(async () => {
|
|
806
|
-
// 预测性渲染
|
|
807
|
-
const nextOp = this.predictor.predict(operation, operations.slice(i + 1));
|
|
808
|
-
if (nextOp) {
|
|
809
|
-
this.predictor.preRender(nextOp);
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// 执行操作
|
|
813
|
-
this.interrupter.currentOperation = this.executor.execute(operation);
|
|
814
|
-
await this.interrupter.currentOperation;
|
|
815
|
-
|
|
816
|
-
// 保存检查点
|
|
817
|
-
this.stateManager.saveCheckpoint(i, operation);
|
|
818
|
-
|
|
819
|
-
// 反馈进度
|
|
820
|
-
if (onProgress) {
|
|
821
|
-
onProgress({
|
|
822
|
-
current: i + 1,
|
|
823
|
-
total: operations.length,
|
|
824
|
-
sessionId: this.stateManager.sessionId,
|
|
825
|
-
backpressure: this.backpressure.getStatus()
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// 提交事务
|
|
832
|
-
await this.executor.commit();
|
|
833
|
-
|
|
834
|
-
return {
|
|
835
|
-
success: true,
|
|
836
|
-
sessionId: this.stateManager.sessionId
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
} catch (error) {
|
|
840
|
-
// 回滚事务
|
|
841
|
-
await this.executor.rollback();
|
|
842
|
-
throw error;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// 中断流式渲染
|
|
847
|
-
interrupt() {
|
|
848
|
-
this.interrupter.interrupt();
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// 切换方向
|
|
852
|
-
async switchDirection(newDirection) {
|
|
853
|
-
await this.switcher.requestSwitch(newDirection);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
```
|
|
857
|
-
|
|
858
|
-
---
|
|
859
|
-
|
|
860
|
-
## 🎯 优化效果预期
|
|
861
|
-
|
|
862
|
-
| 优化项 | 指标 | 优化前 | 优化后 | 提升 |
|
|
863
|
-
|--------|------|--------|--------|------|
|
|
864
|
-
| **稳定性** | 连接成功率 | 95% | 99.9% | +5% |
|
|
865
|
-
| **延迟** | 首次反馈 | 200ms | 50ms | 75% ⬇️ |
|
|
866
|
-
| **性能** | CPU占用 | 30% | 10% | 67% ⬇️ |
|
|
867
|
-
| **准确性** | 操作成功率 | 98% | 99.9% | +2% |
|
|
868
|
-
| **恢复** | 中断恢复率 | 0% | 100% | ∞ ⬆️ |
|
|
869
|
-
|
|
870
|
-
---
|
|
871
|
-
|
|
872
|
-
**状态**: ✅ 设计完成
|
|
873
|
-
**下一步**: 实现集成测试
|
|
874
|
-
**目标**: 生产级稳定性
|