rcoder 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,725 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Rcoder 异步反馈模块
4
+ 针对中转场景和低带宽场景的异步操作反馈机制优化
5
+ """
6
+
7
+ import time
8
+ import asyncio
9
+ import threading
10
+ import queue
11
+ import json
12
+ from typing import Optional, List, Dict, Any, Callable
13
+
14
+ class AsyncFeedback:
15
+ """
16
+ 异步反馈类
17
+ 提供实时进度反馈、断点续传、网络状态监测等功能
18
+ """
19
+
20
+ def __init__(self, remote_host):
21
+ """
22
+ 初始化异步反馈
23
+
24
+ Args:
25
+ remote_host: RemoteHost实例
26
+ """
27
+ self.remote_host = remote_host
28
+ self._progress_queue = queue.Queue()
29
+ self._status_queue = queue.Queue()
30
+ self._result_queue = queue.Queue()
31
+ self._cancel_events = {}
32
+ self._active_tasks = {}
33
+ self._lock = threading.Lock()
34
+ self._network_monitor = None
35
+ self._bandwidth_history = []
36
+ self._last_network_check = 0
37
+ self._network_check_interval = 30 # 网络检查间隔(秒)
38
+
39
+ def _monitor_network(self):
40
+ """
41
+ 监控网络状态
42
+ """
43
+ while True:
44
+ try:
45
+ start_time = time.time()
46
+ # 测试网络延迟
47
+ result = self.remote_host.run("echo 1", use_cache=False)
48
+ end_time = time.time()
49
+ latency = (end_time - start_time) * 1000 # 毫秒
50
+
51
+ # 测试带宽(简化版)
52
+ bandwidth_test = self.remote_host.run("curl -s -w '%{speed_download}' -o /dev/null https://speed.hetzner.de/10MB.bin", use_cache=False)
53
+ try:
54
+ bandwidth = float(bandwidth_test.strip()) / (1024 * 1024) # MB/s
55
+ except:
56
+ bandwidth = 0
57
+
58
+ # 记录网络状态
59
+ network_status = {
60
+ 'timestamp': time.time(),
61
+ 'latency': latency,
62
+ 'bandwidth': bandwidth,
63
+ 'status': 'stable' if latency < 500 and bandwidth > 0.1 else 'unstable'
64
+ }
65
+
66
+ self._bandwidth_history.append(network_status)
67
+ # 只保留最近10条记录
68
+ if len(self._bandwidth_history) > 10:
69
+ self._bandwidth_history.pop(0)
70
+
71
+ # 发送网络状态更新
72
+ self._status_queue.put({
73
+ 'type': 'network_status',
74
+ 'data': network_status
75
+ })
76
+
77
+ except Exception as e:
78
+ # 网络检查失败
79
+ self._status_queue.put({
80
+ 'type': 'network_error',
81
+ 'data': {'error': str(e)}
82
+ })
83
+
84
+ time.sleep(self._network_check_interval)
85
+
86
+ def start_network_monitor(self):
87
+ """
88
+ 启动网络监控
89
+ """
90
+ if not self._network_monitor:
91
+ self._network_monitor = threading.Thread(target=self._monitor_network, daemon=True)
92
+ self._network_monitor.start()
93
+ print("✅ 网络监控已启动")
94
+
95
+ def stop_network_monitor(self):
96
+ """
97
+ 停止网络监控
98
+ """
99
+ if self._network_monitor:
100
+ # 线程是守护线程,会自动退出
101
+ self._network_monitor = None
102
+ print("✅ 网络监控已停止")
103
+
104
+ def get_network_status(self) -> Dict[str, Any]:
105
+ """
106
+ 获取当前网络状态
107
+ """
108
+ if not self._bandwidth_history:
109
+ return {
110
+ 'status': 'unknown',
111
+ 'message': '网络状态尚未检测'
112
+ }
113
+
114
+ latest = self._bandwidth_history[-1]
115
+ return {
116
+ 'status': latest['status'],
117
+ 'latency': f"{latest['latency']:.2f}ms",
118
+ 'bandwidth': f"{latest['bandwidth']:.2f} MB/s",
119
+ 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(latest['timestamp']))
120
+ }
121
+
122
+ def get_bandwidth_trend(self) -> List[Dict[str, Any]]:
123
+ """
124
+ 获取带宽趋势
125
+ """
126
+ return self._bandwidth_history
127
+
128
+ async def run_with_progress(self, command: str, task_id: Optional[str] = None,
129
+ use_cache: bool = False) -> Dict[str, Any]:
130
+ """
131
+ 带进度反馈的异步执行命令
132
+
133
+ Args:
134
+ command: 要执行的命令
135
+ task_id: 任务ID
136
+ use_cache: 是否使用缓存
137
+ """
138
+ if not task_id:
139
+ task_id = f"task_{int(time.time() * 1000)}"
140
+
141
+ # 创建取消事件
142
+ self._cancel_events[task_id] = threading.Event()
143
+
144
+ # 发送任务开始状态
145
+ self._status_queue.put({
146
+ 'type': 'task_started',
147
+ 'data': {
148
+ 'task_id': task_id,
149
+ 'command': command,
150
+ 'timestamp': time.time()
151
+ }
152
+ })
153
+
154
+ # 网络状态检查
155
+ network_status = self.get_network_status()
156
+ self._status_queue.put({
157
+ 'type': 'network_pre_check',
158
+ 'data': network_status
159
+ })
160
+
161
+ # 根据网络状态调整策略
162
+ if network_status['status'] == 'unstable':
163
+ self._status_queue.put({
164
+ 'type': 'warning',
165
+ 'data': {
166
+ 'message': '网络状态不稳定,将启用容错模式',
167
+ 'suggestions': ['启用断点续传', '增加超时时间', '减少并发数']
168
+ }
169
+ })
170
+
171
+ try:
172
+ # 执行命令
173
+ self._active_tasks[task_id] = {
174
+ 'status': 'running',
175
+ 'start_time': time.time(),
176
+ 'command': command
177
+ }
178
+
179
+ # 发送进度更新
180
+ self._progress_queue.put({
181
+ 'task_id': task_id,
182
+ 'progress': 0,
183
+ 'status': '开始执行命令',
184
+ 'timestamp': time.time()
185
+ })
186
+
187
+ # 执行命令(带智能重试)
188
+ result = await self._execute_with_retry(command, task_id, use_cache)
189
+
190
+ # 计算执行时间
191
+ execution_time = time.time() - self._active_tasks[task_id]['start_time']
192
+
193
+ # 更新任务状态
194
+ self._active_tasks[task_id]['status'] = 'completed'
195
+ self._active_tasks[task_id]['end_time'] = time.time()
196
+ self._active_tasks[task_id]['execution_time'] = execution_time
197
+
198
+ # 发送完成状态
199
+ self._progress_queue.put({
200
+ 'task_id': task_id,
201
+ 'progress': 100,
202
+ 'status': '命令执行完成',
203
+ 'timestamp': time.time()
204
+ })
205
+
206
+ # 发送结果
207
+ result_data = {
208
+ 'task_id': task_id,
209
+ 'command': command,
210
+ 'result': result,
211
+ 'execution_time': execution_time,
212
+ 'status': 'success'
213
+ }
214
+
215
+ self._result_queue.put(result_data)
216
+ self._status_queue.put({
217
+ 'type': 'task_completed',
218
+ 'data': result_data
219
+ })
220
+
221
+ return result_data
222
+
223
+ except Exception as e:
224
+ # 发送错误状态
225
+ error_data = {
226
+ 'task_id': task_id,
227
+ 'command': command,
228
+ 'error': str(e),
229
+ 'timestamp': time.time()
230
+ }
231
+
232
+ self._status_queue.put({
233
+ 'type': 'task_failed',
234
+ 'data': error_data
235
+ })
236
+
237
+ return {
238
+ 'task_id': task_id,
239
+ 'command': command,
240
+ 'result': f"错误: {e}",
241
+ 'status': 'error'
242
+ }
243
+ finally:
244
+ # 清理资源
245
+ with self._lock:
246
+ if task_id in self._cancel_events:
247
+ del self._cancel_events[task_id]
248
+ if task_id in self._active_tasks:
249
+ del self._active_tasks[task_id]
250
+
251
+ async def _execute_with_retry(self, command: str, task_id: str,
252
+ use_cache: bool = False, max_retries: int = 3) -> str:
253
+ """
254
+ 带智能重试的命令执行
255
+
256
+ Args:
257
+ command: 要执行的命令
258
+ task_id: 任务ID
259
+ use_cache: 是否使用缓存
260
+ max_retries: 最大重试次数
261
+ """
262
+ retry_count = 0
263
+ base_delay = 1
264
+
265
+ while retry_count <= max_retries:
266
+ try:
267
+ if retry_count > 0:
268
+ # 发送重试状态
269
+ self._status_queue.put({
270
+ 'type': 'retry',
271
+ 'data': {
272
+ 'task_id': task_id,
273
+ 'retry_count': retry_count,
274
+ 'max_retries': max_retries,
275
+ 'timestamp': time.time()
276
+ }
277
+ })
278
+
279
+ # 执行命令
280
+ result = await self.remote_host.run_async(command, use_cache=use_cache)
281
+
282
+ # 发送进度更新
283
+ self._progress_queue.put({
284
+ 'task_id': task_id,
285
+ 'progress': 75,
286
+ 'status': '命令执行中',
287
+ 'timestamp': time.time()
288
+ })
289
+
290
+ return result
291
+
292
+ except Exception as e:
293
+ retry_count += 1
294
+ if retry_count > max_retries:
295
+ raise
296
+
297
+ # 计算退避时间
298
+ delay = base_delay * (2 ** (retry_count - 1))
299
+
300
+ # 发送重试等待状态
301
+ self._status_queue.put({
302
+ 'type': 'retry_wait',
303
+ 'data': {
304
+ 'task_id': task_id,
305
+ 'retry_count': retry_count,
306
+ 'delay': delay,
307
+ 'error': str(e),
308
+ 'timestamp': time.time()
309
+ }
310
+ })
311
+
312
+ # 等待重试
313
+ await asyncio.sleep(delay)
314
+
315
+ async def download_with_progress(self, url: str, destination: str,
316
+ task_id: Optional[str] = None) -> Dict[str, Any]:
317
+ """
318
+ 带进度反馈的异步下载
319
+
320
+ Args:
321
+ url: 下载URL
322
+ destination: 保存路径
323
+ task_id: 任务ID
324
+ """
325
+ if not task_id:
326
+ task_id = f"download_{int(time.time() * 1000)}"
327
+
328
+ # 创建取消事件
329
+ self._cancel_events[task_id] = threading.Event()
330
+
331
+ # 发送任务开始状态
332
+ self._status_queue.put({
333
+ 'type': 'download_started',
334
+ 'data': {
335
+ 'task_id': task_id,
336
+ 'url': url,
337
+ 'destination': destination,
338
+ 'timestamp': time.time()
339
+ }
340
+ })
341
+
342
+ try:
343
+ # 检查下载工具
344
+ has_wget = 'wget' in self.remote_host.run('which wget', use_cache=True)
345
+ has_curl = 'curl' in self.remote_host.run('which curl', use_cache=True)
346
+
347
+ if not has_wget and not has_curl:
348
+ raise Exception("远程主机未安装wget或curl")
349
+
350
+ # 构建下载命令(带进度)
351
+ if has_wget:
352
+ # 使用wget下载,带进度
353
+ download_cmd = f"wget -c --tries=10 --timeout=60 --progress=bar:force '{url}' -O '{destination}' 2>&1 | tail -n 1"
354
+ else:
355
+ # 使用curl下载,带进度
356
+ download_cmd = f"curl -L --retry 10 --connect-timeout 60 --progress-bar '{url}' -o '{destination}'"
357
+
358
+ # 执行下载
359
+ self._active_tasks[task_id] = {
360
+ 'status': 'downloading',
361
+ 'start_time': time.time(),
362
+ 'url': url,
363
+ 'destination': destination
364
+ }
365
+
366
+ # 发送进度更新
367
+ self._progress_queue.put({
368
+ 'task_id': task_id,
369
+ 'progress': 0,
370
+ 'status': '开始下载',
371
+ 'timestamp': time.time()
372
+ })
373
+
374
+ # 执行下载命令
375
+ result = await self._execute_with_retry(download_cmd, task_id, use_cache=False)
376
+
377
+ # 计算下载时间
378
+ download_time = time.time() - self._active_tasks[task_id]['start_time']
379
+
380
+ # 检查文件是否下载成功
381
+ file_check = await self.remote_host.run_async(f"ls -la '{destination}'", use_cache=False)
382
+
383
+ # 更新任务状态
384
+ self._active_tasks[task_id]['status'] = 'completed'
385
+ self._active_tasks[task_id]['end_time'] = time.time()
386
+ self._active_tasks[task_id]['download_time'] = download_time
387
+
388
+ # 发送完成状态
389
+ self._progress_queue.put({
390
+ 'task_id': task_id,
391
+ 'progress': 100,
392
+ 'status': '下载完成',
393
+ 'timestamp': time.time()
394
+ })
395
+
396
+ # 计算下载速度
397
+ file_size = 0
398
+ try:
399
+ size_cmd = await self.remote_host.run_async(f"du -b '{destination}'", use_cache=False)
400
+ file_size = int(size_cmd.split()[0])
401
+ speed = (file_size / (1024 * 1024)) / download_time # MB/s
402
+ except:
403
+ speed = 0
404
+
405
+ # 发送结果
406
+ result_data = {
407
+ 'task_id': task_id,
408
+ 'url': url,
409
+ 'destination': destination,
410
+ 'file_size': f"{file_size / (1024 * 1024):.2f} MB" if file_size > 0 else '未知',
411
+ 'download_time': f"{download_time:.2f}秒",
412
+ 'speed': f"{speed:.2f} MB/s" if speed > 0 else '未知',
413
+ 'status': 'success',
414
+ 'timestamp': time.time()
415
+ }
416
+
417
+ self._result_queue.put(result_data)
418
+ self._status_queue.put({
419
+ 'type': 'download_completed',
420
+ 'data': result_data
421
+ })
422
+
423
+ return result_data
424
+
425
+ except Exception as e:
426
+ # 发送错误状态
427
+ error_data = {
428
+ 'task_id': task_id,
429
+ 'url': url,
430
+ 'error': str(e),
431
+ 'timestamp': time.time()
432
+ }
433
+
434
+ self._status_queue.put({
435
+ 'type': 'download_failed',
436
+ 'data': error_data
437
+ })
438
+
439
+ return {
440
+ 'task_id': task_id,
441
+ 'url': url,
442
+ 'error': str(e),
443
+ 'status': 'error'
444
+ }
445
+ finally:
446
+ # 清理资源
447
+ with self._lock:
448
+ if task_id in self._cancel_events:
449
+ del self._cancel_events[task_id]
450
+ if task_id in self._active_tasks:
451
+ del self._active_tasks[task_id]
452
+
453
+ async def batch_with_progress(self, commands: List[str], task_id: Optional[str] = None,
454
+ use_cache: bool = False) -> Dict[str, Any]:
455
+ """
456
+ 带进度反馈的异步批量执行
457
+
458
+ Args:
459
+ commands: 命令列表
460
+ task_id: 任务ID
461
+ use_cache: 是否使用缓存
462
+ """
463
+ if not task_id:
464
+ task_id = f"batch_{int(time.time() * 1000)}"
465
+
466
+ # 创建取消事件
467
+ self._cancel_events[task_id] = threading.Event()
468
+
469
+ # 发送任务开始状态
470
+ self._status_queue.put({
471
+ 'type': 'batch_started',
472
+ 'data': {
473
+ 'task_id': task_id,
474
+ 'command_count': len(commands),
475
+ 'timestamp': time.time()
476
+ }
477
+ })
478
+
479
+ try:
480
+ self._active_tasks[task_id] = {
481
+ 'status': 'running',
482
+ 'start_time': time.time(),
483
+ 'command_count': len(commands),
484
+ 'completed_count': 0
485
+ }
486
+
487
+ results = {}
488
+ total_commands = len(commands)
489
+
490
+ # 发送初始进度
491
+ self._progress_queue.put({
492
+ 'task_id': task_id,
493
+ 'progress': 0,
494
+ 'status': f'开始执行 {total_commands} 个命令',
495
+ 'timestamp': time.time()
496
+ })
497
+
498
+ # 逐个执行命令
499
+ for i, command in enumerate(commands):
500
+ if self._cancel_events[task_id].is_set():
501
+ raise Exception("任务已取消")
502
+
503
+ # 发送命令开始状态
504
+ self._status_queue.put({
505
+ 'type': 'command_started',
506
+ 'data': {
507
+ 'task_id': task_id,
508
+ 'command_index': i,
509
+ 'command': command,
510
+ 'timestamp': time.time()
511
+ }
512
+ })
513
+
514
+ # 执行命令
515
+ try:
516
+ cmd_result = await self.remote_host.run_async(command, use_cache=use_cache)
517
+ results[command] = cmd_result
518
+
519
+ # 发送命令完成状态
520
+ self._status_queue.put({
521
+ 'type': 'command_completed',
522
+ 'data': {
523
+ 'task_id': task_id,
524
+ 'command_index': i,
525
+ 'command': command,
526
+ 'success': True,
527
+ 'timestamp': time.time()
528
+ }
529
+ })
530
+ except Exception as e:
531
+ results[command] = f"错误: {e}"
532
+
533
+ # 发送命令失败状态
534
+ self._status_queue.put({
535
+ 'type': 'command_failed',
536
+ 'data': {
537
+ 'task_id': task_id,
538
+ 'command_index': i,
539
+ 'command': command,
540
+ 'error': str(e),
541
+ 'timestamp': time.time()
542
+ }
543
+ })
544
+
545
+ # 更新完成计数
546
+ completed_count = i + 1
547
+ progress = int((completed_count / total_commands) * 100)
548
+
549
+ # 更新任务状态
550
+ self._active_tasks[task_id]['completed_count'] = completed_count
551
+
552
+ # 发送进度更新
553
+ self._progress_queue.put({
554
+ 'task_id': task_id,
555
+ 'progress': progress,
556
+ 'status': f'已完成 {completed_count}/{total_commands} 个命令',
557
+ 'timestamp': time.time()
558
+ })
559
+
560
+ # 计算执行时间
561
+ execution_time = time.time() - self._active_tasks[task_id]['start_time']
562
+
563
+ # 更新任务状态
564
+ self._active_tasks[task_id]['status'] = 'completed'
565
+ self._active_tasks[task_id]['end_time'] = time.time()
566
+ self._active_tasks[task_id]['execution_time'] = execution_time
567
+
568
+ # 发送完成状态
569
+ self._progress_queue.put({
570
+ 'task_id': task_id,
571
+ 'progress': 100,
572
+ 'status': '批量执行完成',
573
+ 'timestamp': time.time()
574
+ })
575
+
576
+ # 发送结果
577
+ result_data = {
578
+ 'task_id': task_id,
579
+ 'command_count': total_commands,
580
+ 'execution_time': f"{execution_time:.2f}秒",
581
+ 'results': results,
582
+ 'status': 'success',
583
+ 'timestamp': time.time()
584
+ }
585
+
586
+ self._result_queue.put(result_data)
587
+ self._status_queue.put({
588
+ 'type': 'batch_completed',
589
+ 'data': result_data
590
+ })
591
+
592
+ return result_data
593
+
594
+ except Exception as e:
595
+ # 发送错误状态
596
+ error_data = {
597
+ 'task_id': task_id,
598
+ 'error': str(e),
599
+ 'timestamp': time.time()
600
+ }
601
+
602
+ self._status_queue.put({
603
+ 'type': 'batch_failed',
604
+ 'data': error_data
605
+ })
606
+
607
+ return {
608
+ 'task_id': task_id,
609
+ 'error': str(e),
610
+ 'status': 'error'
611
+ }
612
+ finally:
613
+ # 清理资源
614
+ with self._lock:
615
+ if task_id in self._cancel_events:
616
+ del self._cancel_events[task_id]
617
+ if task_id in self._active_tasks:
618
+ del self._active_tasks[task_id]
619
+
620
+ def cancel_task(self, task_id: str) -> bool:
621
+ """
622
+ 取消任务
623
+
624
+ Args:
625
+ task_id: 任务ID
626
+ """
627
+ if task_id in self._cancel_events:
628
+ self._cancel_events[task_id].set()
629
+
630
+ # 发送取消状态
631
+ self._status_queue.put({
632
+ 'type': 'task_cancelled',
633
+ 'data': {
634
+ 'task_id': task_id,
635
+ 'timestamp': time.time()
636
+ }
637
+ })
638
+
639
+ return True
640
+ return False
641
+
642
+ def get_active_tasks(self) -> Dict[str, Any]:
643
+ """
644
+ 获取活跃任务
645
+ """
646
+ return self._active_tasks
647
+
648
+ def get_progress(self, task_id: Optional[str] = None) -> List[Dict[str, Any]]:
649
+ """
650
+ 获取任务进度
651
+
652
+ Args:
653
+ task_id: 任务ID,None表示获取所有任务进度
654
+ """
655
+ progress_items = []
656
+ while not self._progress_queue.empty():
657
+ item = self._progress_queue.get()
658
+ if not task_id or item['task_id'] == task_id:
659
+ progress_items.append(item)
660
+ return progress_items
661
+
662
+ def get_status_updates(self) -> List[Dict[str, Any]]:
663
+ """
664
+ 获取状态更新
665
+ """
666
+ status_items = []
667
+ while not self._status_queue.empty():
668
+ status_items.append(self._status_queue.get())
669
+ return status_items
670
+
671
+ def get_results(self, task_id: Optional[str] = None) -> List[Dict[str, Any]]:
672
+ """
673
+ 获取任务结果
674
+
675
+ Args:
676
+ task_id: 任务ID,None表示获取所有结果
677
+ """
678
+ result_items = []
679
+ while not self._result_queue.empty():
680
+ item = self._result_queue.get()
681
+ if not task_id or item['task_id'] == task_id:
682
+ result_items.append(item)
683
+ return result_items
684
+
685
+ def clear_queues(self):
686
+ """
687
+ 清空队列
688
+ """
689
+ # 清空进度队列
690
+ while not self._progress_queue.empty():
691
+ self._progress_queue.get()
692
+
693
+ # 清空状态队列
694
+ while not self._status_queue.empty():
695
+ self._status_queue.get()
696
+
697
+ # 清空结果队列
698
+ while not self._result_queue.empty():
699
+ self._result_queue.get()
700
+
701
+ def shutdown(self):
702
+ """
703
+ 关闭异步反馈模块
704
+ """
705
+ # 停止网络监控
706
+ self.stop_network_monitor()
707
+
708
+ # 取消所有活跃任务
709
+ for task_id in list(self._active_tasks.keys()):
710
+ self.cancel_task(task_id)
711
+
712
+ # 清空队列
713
+ self.clear_queues()
714
+
715
+ print("✅ 异步反馈模块已关闭")
716
+
717
+
718
+ def get_async_feedback(remote_host) -> AsyncFeedback:
719
+ """
720
+ 获取异步反馈实例
721
+
722
+ Args:
723
+ remote_host: RemoteHost实例
724
+ """
725
+ return AsyncFeedback(remote_host)