@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,611 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 生产级 PPT 流式渲染器
|
|
3
|
-
*
|
|
4
|
-
* 核心特性:
|
|
5
|
-
* - 稳定性:自动重连、心跳检测、消息确认
|
|
6
|
-
* - 断点续传:状态持久化、会话恢复
|
|
7
|
-
* - 性能优化:缓冲区、反压控制、预测渲染
|
|
8
|
-
* - 准确性:操作验证、事务回滚
|
|
9
|
-
* - 中断处理:优雅收尾、方向切换
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { EventEmitter } from 'node:events';
|
|
13
|
-
|
|
14
|
-
// ============================================
|
|
15
|
-
// 1. 稳定连接管理
|
|
16
|
-
// ============================================
|
|
17
|
-
|
|
18
|
-
export class StableConnection extends EventEmitter {
|
|
19
|
-
constructor(wsUrl, options = {}) {
|
|
20
|
-
super();
|
|
21
|
-
this.wsUrl = wsUrl;
|
|
22
|
-
this.ws = null;
|
|
23
|
-
this.reconnectAttempts = 0;
|
|
24
|
-
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
|
|
25
|
-
this.reconnectDelay = options.reconnectDelay || 1000;
|
|
26
|
-
this.heartbeatInterval = null;
|
|
27
|
-
this.lastHeartbeat = Date.now();
|
|
28
|
-
this.pendingMessages = [];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async connect() {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
try {
|
|
34
|
-
this.ws = new WebSocket(this.wsUrl);
|
|
35
|
-
|
|
36
|
-
this.ws.onopen = () => {
|
|
37
|
-
console.log('✅ WebSocket 连接成功');
|
|
38
|
-
this.reconnectAttempts = 0;
|
|
39
|
-
this.startHeartbeat();
|
|
40
|
-
this.flushPendingMessages();
|
|
41
|
-
this.emit('connected');
|
|
42
|
-
resolve();
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
this.ws.onerror = (error) => {
|
|
46
|
-
console.error('❌ WebSocket 错误:', error);
|
|
47
|
-
this.emit('error', error);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
this.ws.onclose = () => {
|
|
51
|
-
console.log('🔌 WebSocket 断开');
|
|
52
|
-
this.stopHeartbeat();
|
|
53
|
-
this.emit('disconnected');
|
|
54
|
-
this.attemptReconnect();
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
this.ws.onmessage = (event) => {
|
|
58
|
-
this.handleMessage(event.data);
|
|
59
|
-
};
|
|
60
|
-
} catch (error) {
|
|
61
|
-
reject(error);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
startHeartbeat() {
|
|
67
|
-
this.heartbeatInterval = setInterval(() => {
|
|
68
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
69
|
-
this.send({ type: 'ping', timestamp: Date.now() });
|
|
70
|
-
} else {
|
|
71
|
-
this.attemptReconnect();
|
|
72
|
-
}
|
|
73
|
-
}, 5000);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
stopHeartbeat() {
|
|
77
|
-
if (this.heartbeatInterval) {
|
|
78
|
-
clearInterval(this.heartbeatInterval);
|
|
79
|
-
this.heartbeatInterval = null;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async attemptReconnect() {
|
|
84
|
-
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
85
|
-
console.error('❌ 达到最大重连次数');
|
|
86
|
-
this.emit('max_reconnect_attempts');
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.reconnectAttempts++;
|
|
91
|
-
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
92
|
-
|
|
93
|
-
console.log(`🔄 重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts}) 延迟 ${delay}ms`);
|
|
94
|
-
|
|
95
|
-
await new Promise(r => setTimeout(r, delay));
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
await this.connect();
|
|
99
|
-
console.log('✅ 重连成功');
|
|
100
|
-
} catch (error) {
|
|
101
|
-
console.error('❌ 重连失败:', error);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
send(data) {
|
|
106
|
-
const message = JSON.stringify(data);
|
|
107
|
-
|
|
108
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
109
|
-
this.ws.send(message);
|
|
110
|
-
return true;
|
|
111
|
-
} else {
|
|
112
|
-
// 连接未就绪,缓存消息
|
|
113
|
-
this.pendingMessages.push(message);
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
flushPendingMessages() {
|
|
119
|
-
while (this.pendingMessages.length > 0) {
|
|
120
|
-
const message = this.pendingMessages.shift();
|
|
121
|
-
this.ws.send(message);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
handleMessage(data) {
|
|
126
|
-
try {
|
|
127
|
-
const message = JSON.parse(data);
|
|
128
|
-
|
|
129
|
-
if (message.type === 'pong') {
|
|
130
|
-
this.lastHeartbeat = Date.now();
|
|
131
|
-
} else if (message.type === 'ack') {
|
|
132
|
-
this.emit('ack', message.messageId);
|
|
133
|
-
} else {
|
|
134
|
-
this.emit('message', message);
|
|
135
|
-
}
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('解析消息失败:', error);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
close() {
|
|
142
|
-
this.stopHeartbeat();
|
|
143
|
-
if (this.ws) {
|
|
144
|
-
this.ws.close();
|
|
145
|
-
this.ws = null;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// ============================================
|
|
151
|
-
// 2. 状态管理与断点续传
|
|
152
|
-
// ============================================
|
|
153
|
-
|
|
154
|
-
export class StreamingStateManager {
|
|
155
|
-
constructor() {
|
|
156
|
-
this.sessionId = this.generateSessionId();
|
|
157
|
-
this.currentIndex = 0;
|
|
158
|
-
this.completedOps = new Set();
|
|
159
|
-
this.failedOps = new Set();
|
|
160
|
-
this.operations = [];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
generateSessionId() {
|
|
164
|
-
return `stream_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
saveCheckpoint(index, operation, result) {
|
|
168
|
-
this.currentIndex = index;
|
|
169
|
-
this.completedOps.add(index);
|
|
170
|
-
|
|
171
|
-
const state = {
|
|
172
|
-
sessionId: this.sessionId,
|
|
173
|
-
currentIndex: this.currentIndex,
|
|
174
|
-
completedOps: Array.from(this.completedOps),
|
|
175
|
-
failedOps: Array.from(this.failedOps),
|
|
176
|
-
totalOps: this.operations.length,
|
|
177
|
-
timestamp: Date.now()
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
// 持久化(可选)
|
|
181
|
-
if (typeof localStorage !== 'undefined') {
|
|
182
|
-
localStorage.setItem(`ppt_stream_${this.sessionId}`, JSON.stringify(state));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
restoreState(sessionId) {
|
|
187
|
-
if (typeof localStorage === 'undefined') return null;
|
|
188
|
-
|
|
189
|
-
const stored = localStorage.getItem(`ppt_stream_${sessionId}`);
|
|
190
|
-
if (stored) {
|
|
191
|
-
const state = JSON.parse(stored);
|
|
192
|
-
this.sessionId = state.sessionId;
|
|
193
|
-
this.currentIndex = state.currentIndex;
|
|
194
|
-
this.completedOps = new Set(state.completedOps);
|
|
195
|
-
this.failedOps = new Set(state.failedOps);
|
|
196
|
-
return state;
|
|
197
|
-
}
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
isCompleted(index) {
|
|
202
|
-
return this.completedOps.has(index);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
markFailed(index) {
|
|
206
|
-
this.failedOps.add(index);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
clearState() {
|
|
210
|
-
if (typeof localStorage !== 'undefined') {
|
|
211
|
-
localStorage.removeItem(`ppt_stream_${this.sessionId}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
getProgress() {
|
|
216
|
-
return {
|
|
217
|
-
current: this.currentIndex,
|
|
218
|
-
completed: this.completedOps.size,
|
|
219
|
-
failed: this.failedOps.size,
|
|
220
|
-
total: this.operations.length
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// ============================================
|
|
226
|
-
// 3. 反压控制
|
|
227
|
-
// ============================================
|
|
228
|
-
|
|
229
|
-
export class BackpressureController {
|
|
230
|
-
constructor(options = {}) {
|
|
231
|
-
this.maxInFlight = options.maxInFlight || 3;
|
|
232
|
-
this.inFlight = 0;
|
|
233
|
-
this.queue = [];
|
|
234
|
-
this.paused = false;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async execute(fn) {
|
|
238
|
-
while (this.inFlight >= this.maxInFlight || this.paused) {
|
|
239
|
-
await new Promise(resolve => this.queue.push(resolve));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
this.inFlight++;
|
|
243
|
-
|
|
244
|
-
try {
|
|
245
|
-
return await fn();
|
|
246
|
-
} finally {
|
|
247
|
-
this.inFlight--;
|
|
248
|
-
this.resume();
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
pause() {
|
|
253
|
-
this.paused = true;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
resume() {
|
|
257
|
-
if (this.paused) return;
|
|
258
|
-
|
|
259
|
-
while (this.queue.length > 0 && this.inFlight < this.maxInFlight) {
|
|
260
|
-
const resolve = this.queue.shift();
|
|
261
|
-
resolve();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
getStatus() {
|
|
266
|
-
return {
|
|
267
|
-
inFlight: this.inFlight,
|
|
268
|
-
queued: this.queue.length,
|
|
269
|
-
paused: this.paused
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ============================================
|
|
275
|
-
// 4. 优雅中断处理
|
|
276
|
-
// ============================================
|
|
277
|
-
|
|
278
|
-
export class GracefulInterrupter {
|
|
279
|
-
constructor() {
|
|
280
|
-
this.interrupted = false;
|
|
281
|
-
this.currentOperation = null;
|
|
282
|
-
this.completedOps = [];
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
interrupt() {
|
|
286
|
-
console.log('⚠️ 收到中断请求');
|
|
287
|
-
this.interrupted = true;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
shouldInterrupt() {
|
|
291
|
-
return this.interrupted;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async gracefulShutdown(stateManager) {
|
|
295
|
-
console.log('🔚 开始优雅收尾...');
|
|
296
|
-
|
|
297
|
-
// 等待当前操作完成
|
|
298
|
-
if (this.currentOperation) {
|
|
299
|
-
console.log(' ⏳ 等待当前操作完成...');
|
|
300
|
-
try {
|
|
301
|
-
await this.currentOperation;
|
|
302
|
-
} catch (error) {
|
|
303
|
-
console.log(' ⚠️ 当前操作失败,但继续收尾');
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// 保存进度
|
|
308
|
-
console.log(' 💾 保存进度...');
|
|
309
|
-
const progress = stateManager.getProgress();
|
|
310
|
-
|
|
311
|
-
console.log(`✅ 优雅收尾完成 (已完成 ${progress.completed}/${progress.total})`);
|
|
312
|
-
|
|
313
|
-
return progress;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
reset() {
|
|
317
|
-
this.interrupted = false;
|
|
318
|
-
this.currentOperation = null;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// ============================================
|
|
323
|
-
// 5. 生产级流式渲染器
|
|
324
|
-
// ============================================
|
|
325
|
-
|
|
326
|
-
export class ProductionStreamingRenderer extends EventEmitter {
|
|
327
|
-
constructor(options = {}) {
|
|
328
|
-
super();
|
|
329
|
-
|
|
330
|
-
// 连接管理
|
|
331
|
-
this.connection = new StableConnection(
|
|
332
|
-
options.wsUrl || 'ws://127.0.0.1:39311/ws',
|
|
333
|
-
options.connection
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
// 状态管理
|
|
337
|
-
this.stateManager = new StreamingStateManager();
|
|
338
|
-
|
|
339
|
-
// 性能控制
|
|
340
|
-
this.backpressure = new BackpressureController(options.backpressure);
|
|
341
|
-
|
|
342
|
-
// 中断处理
|
|
343
|
-
this.interrupter = new GracefulInterrupter();
|
|
344
|
-
|
|
345
|
-
// 统计
|
|
346
|
-
this.stats = {
|
|
347
|
-
operations: 0,
|
|
348
|
-
successful: 0,
|
|
349
|
-
failed: 0,
|
|
350
|
-
retried: 0,
|
|
351
|
-
totalTime: 0
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
// 监听连接事件
|
|
355
|
-
this.connection.on('disconnected', () => {
|
|
356
|
-
this.emit('connection_lost');
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
this.connection.on('connected', () => {
|
|
360
|
-
this.emit('connection_restored');
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* 流式编辑(生产级)
|
|
366
|
-
*/
|
|
367
|
-
async streamEdit(operations, onProgress, options = {}) {
|
|
368
|
-
const startTime = Date.now();
|
|
369
|
-
|
|
370
|
-
// 保存操作列表
|
|
371
|
-
this.stateManager.operations = operations;
|
|
372
|
-
|
|
373
|
-
// 连接
|
|
374
|
-
if (!this.connection.ws || this.connection.ws.readyState !== WebSocket.OPEN) {
|
|
375
|
-
console.log('🔌 建立 WebSocket 连接...');
|
|
376
|
-
await this.connection.connect();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// 恢复会话(如果指定)
|
|
380
|
-
if (options.resumeSessionId) {
|
|
381
|
-
const restored = this.stateManager.restoreState(options.resumeSessionId);
|
|
382
|
-
if (restored) {
|
|
383
|
-
console.log(`🔄 恢复会话: ${options.resumeSessionId}`);
|
|
384
|
-
console.log(` 已完成: ${restored.completedOps.length}/${restored.totalOps}`);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const total = operations.length;
|
|
389
|
-
let firstOpTime = 0;
|
|
390
|
-
|
|
391
|
-
console.log(`\n🎬 开始生产级流式渲染 (${total} 个操作)\n`);
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
for (let i = 0; i < operations.length; i++) {
|
|
395
|
-
// 检查中断
|
|
396
|
-
if (this.interrupter.shouldInterrupt()) {
|
|
397
|
-
const progress = await this.interrupter.gracefulShutdown(this.stateManager);
|
|
398
|
-
|
|
399
|
-
if (onProgress) {
|
|
400
|
-
onProgress({
|
|
401
|
-
...progress,
|
|
402
|
-
status: 'interrupted',
|
|
403
|
-
sessionId: this.stateManager.sessionId,
|
|
404
|
-
message: '操作已中断,可使用 sessionId 恢复'
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
break;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// 跳过已完成的操作(断点续传)
|
|
412
|
-
if (this.stateManager.isCompleted(i)) {
|
|
413
|
-
console.log(` ⏭️ 跳过已完成 #${i}`);
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const operation = operations[i];
|
|
418
|
-
const opStartTime = Date.now();
|
|
419
|
-
|
|
420
|
-
// 反压控制
|
|
421
|
-
await this.backpressure.execute(async () => {
|
|
422
|
-
this.stats.operations++;
|
|
423
|
-
|
|
424
|
-
try {
|
|
425
|
-
// 乐观更新(立即渲染)
|
|
426
|
-
this.connection.send({
|
|
427
|
-
type: 'optimistic_update',
|
|
428
|
-
index: i,
|
|
429
|
-
operation: operation.type,
|
|
430
|
-
data: operation.data,
|
|
431
|
-
timestamp: Date.now()
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
// 执行实际操作
|
|
435
|
-
this.interrupter.currentOperation = this._executeOperation(operation);
|
|
436
|
-
await this.interrupter.currentOperation;
|
|
437
|
-
|
|
438
|
-
// 保存检查点
|
|
439
|
-
this.stateManager.saveCheckpoint(i, operation);
|
|
440
|
-
this.stats.successful++;
|
|
441
|
-
|
|
442
|
-
// 确认更新
|
|
443
|
-
this.connection.send({
|
|
444
|
-
type: 'operation_confirmed',
|
|
445
|
-
index: i,
|
|
446
|
-
operation: operation.type,
|
|
447
|
-
success: true
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
// 记录首次操作时间
|
|
451
|
-
if (i === 0) {
|
|
452
|
-
firstOpTime = Date.now() - startTime;
|
|
453
|
-
console.log(` ⚡ 首次操作: ${firstOpTime}ms`);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
console.log(` ✓ #${i} ${operation.type} (${Date.now() - opStartTime}ms)`);
|
|
457
|
-
|
|
458
|
-
} catch (error) {
|
|
459
|
-
this.stats.failed++;
|
|
460
|
-
this.stateManager.markFailed(i);
|
|
461
|
-
|
|
462
|
-
console.error(` ✗ #${i} ${operation.type} 失败:`, error.message);
|
|
463
|
-
|
|
464
|
-
// 回滚更新
|
|
465
|
-
this.connection.send({
|
|
466
|
-
type: 'rollback_update',
|
|
467
|
-
index: i,
|
|
468
|
-
operation: operation.type
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
// 根据策略决定是否继续
|
|
472
|
-
if (options.stopOnError) {
|
|
473
|
-
throw error;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
// 反馈进度
|
|
479
|
-
if (onProgress) {
|
|
480
|
-
const progress = this.stateManager.getProgress();
|
|
481
|
-
onProgress({
|
|
482
|
-
current: i + 1,
|
|
483
|
-
total,
|
|
484
|
-
completed: progress.completed,
|
|
485
|
-
failed: progress.failed,
|
|
486
|
-
progress: ((i + 1) / total * 100).toFixed(1),
|
|
487
|
-
sessionId: this.stateManager.sessionId,
|
|
488
|
-
backpressure: this.backpressure.getStatus()
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// 小延迟让用户看清
|
|
493
|
-
if (i < operations.length - 1 && !options.noDelay) {
|
|
494
|
-
await new Promise(r => setTimeout(r, 50));
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const totalTime = Date.now() - startTime;
|
|
499
|
-
this.stats.totalTime = totalTime;
|
|
500
|
-
|
|
501
|
-
console.log(`\n✅ 流式渲染完成`);
|
|
502
|
-
console.log(` 总耗时: ${totalTime}ms`);
|
|
503
|
-
console.log(` 首次操作: ${firstOpTime}ms`);
|
|
504
|
-
console.log(` 成功: ${this.stats.successful}`);
|
|
505
|
-
console.log(` 失败: ${this.stats.failed}\n`);
|
|
506
|
-
|
|
507
|
-
// 清理状态
|
|
508
|
-
this.stateManager.clearState();
|
|
509
|
-
|
|
510
|
-
return {
|
|
511
|
-
success: true,
|
|
512
|
-
sessionId: this.stateManager.sessionId,
|
|
513
|
-
stats: this.getStats(),
|
|
514
|
-
totalTime,
|
|
515
|
-
firstOpTime
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
} catch (error) {
|
|
519
|
-
console.error('❌ 流式渲染失败:', error);
|
|
520
|
-
|
|
521
|
-
// 保留状态以便恢复
|
|
522
|
-
const progress = this.stateManager.getProgress();
|
|
523
|
-
|
|
524
|
-
return {
|
|
525
|
-
success: false,
|
|
526
|
-
error: error.message,
|
|
527
|
-
sessionId: this.stateManager.sessionId,
|
|
528
|
-
progress,
|
|
529
|
-
canResume: true
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/**
|
|
535
|
-
* 执行操作(带重试)
|
|
536
|
-
*/
|
|
537
|
-
async _executeOperation(operation, retries = 2) {
|
|
538
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
539
|
-
try {
|
|
540
|
-
// 模拟实际操作
|
|
541
|
-
await new Promise((resolve, reject) => {
|
|
542
|
-
setTimeout(() => {
|
|
543
|
-
// 5% 失败率模拟
|
|
544
|
-
if (Math.random() < 0.05) {
|
|
545
|
-
reject(new Error('Operation failed'));
|
|
546
|
-
} else {
|
|
547
|
-
resolve();
|
|
548
|
-
}
|
|
549
|
-
}, Math.random() * 50 + 30);
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
return;
|
|
553
|
-
} catch (error) {
|
|
554
|
-
if (attempt < retries) {
|
|
555
|
-
this.stats.retried++;
|
|
556
|
-
console.log(` 🔄 重试 ${attempt + 1}/${retries}`);
|
|
557
|
-
await new Promise(r => setTimeout(r, 100 * (attempt + 1)));
|
|
558
|
-
} else {
|
|
559
|
-
throw error;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* 中断流式渲染
|
|
567
|
-
*/
|
|
568
|
-
interrupt() {
|
|
569
|
-
this.interrupter.interrupt();
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* 暂停/恢复
|
|
574
|
-
*/
|
|
575
|
-
pause() {
|
|
576
|
-
this.backpressure.pause();
|
|
577
|
-
console.log('⏸️ 暂停流式渲染');
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
resume() {
|
|
581
|
-
this.backpressure.resume();
|
|
582
|
-
console.log('▶️ 恢复流式渲染');
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
* 获取统计信息
|
|
587
|
-
*/
|
|
588
|
-
getStats() {
|
|
589
|
-
return {
|
|
590
|
-
...this.stats,
|
|
591
|
-
successRate: this.stats.operations > 0 ?
|
|
592
|
-
(this.stats.successful / this.stats.operations * 100).toFixed(1) + '%' : '0%',
|
|
593
|
-
avgTime: this.stats.operations > 0 ?
|
|
594
|
-
(this.stats.totalTime / this.stats.operations).toFixed(0) + 'ms' : '0ms'
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* 关闭连接
|
|
600
|
-
*/
|
|
601
|
-
close() {
|
|
602
|
-
this.connection.close();
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* 创建生产级流式渲染器
|
|
608
|
-
*/
|
|
609
|
-
export function createProductionRenderer(options = {}) {
|
|
610
|
-
return new ProductionStreamingRenderer(options);
|
|
611
|
-
}
|