maque 0.2.1__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.
Files changed (143) hide show
  1. maque/__init__.py +30 -0
  2. maque/__main__.py +926 -0
  3. maque/ai_platform/__init__.py +0 -0
  4. maque/ai_platform/crawl.py +45 -0
  5. maque/ai_platform/metrics.py +258 -0
  6. maque/ai_platform/nlp_preprocess.py +67 -0
  7. maque/ai_platform/webpage_screen_shot.py +195 -0
  8. maque/algorithms/__init__.py +78 -0
  9. maque/algorithms/bezier.py +15 -0
  10. maque/algorithms/bktree.py +117 -0
  11. maque/algorithms/core.py +104 -0
  12. maque/algorithms/hilbert.py +16 -0
  13. maque/algorithms/rate_function.py +92 -0
  14. maque/algorithms/transform.py +27 -0
  15. maque/algorithms/trie.py +272 -0
  16. maque/algorithms/utils.py +63 -0
  17. maque/algorithms/video.py +587 -0
  18. maque/api/__init__.py +1 -0
  19. maque/api/common.py +110 -0
  20. maque/api/fetch.py +26 -0
  21. maque/api/static/icon.png +0 -0
  22. maque/api/static/redoc.standalone.js +1782 -0
  23. maque/api/static/swagger-ui-bundle.js +3 -0
  24. maque/api/static/swagger-ui.css +3 -0
  25. maque/cli/__init__.py +1 -0
  26. maque/cli/clean_invisible_chars.py +324 -0
  27. maque/cli/core.py +34 -0
  28. maque/cli/groups/__init__.py +26 -0
  29. maque/cli/groups/config.py +205 -0
  30. maque/cli/groups/data.py +615 -0
  31. maque/cli/groups/doctor.py +259 -0
  32. maque/cli/groups/embedding.py +222 -0
  33. maque/cli/groups/git.py +29 -0
  34. maque/cli/groups/help.py +410 -0
  35. maque/cli/groups/llm.py +223 -0
  36. maque/cli/groups/mcp.py +241 -0
  37. maque/cli/groups/mllm.py +1795 -0
  38. maque/cli/groups/mllm_simple.py +60 -0
  39. maque/cli/groups/quant.py +210 -0
  40. maque/cli/groups/service.py +490 -0
  41. maque/cli/groups/system.py +570 -0
  42. maque/cli/mllm_run.py +1451 -0
  43. maque/cli/script.py +52 -0
  44. maque/cli/tree.py +49 -0
  45. maque/clustering/__init__.py +52 -0
  46. maque/clustering/analyzer.py +347 -0
  47. maque/clustering/clusterers.py +464 -0
  48. maque/clustering/sampler.py +134 -0
  49. maque/clustering/visualizer.py +205 -0
  50. maque/constant.py +13 -0
  51. maque/core.py +133 -0
  52. maque/cv/__init__.py +1 -0
  53. maque/cv/image.py +219 -0
  54. maque/cv/utils.py +68 -0
  55. maque/cv/video/__init__.py +3 -0
  56. maque/cv/video/keyframe_extractor.py +368 -0
  57. maque/embedding/__init__.py +43 -0
  58. maque/embedding/base.py +56 -0
  59. maque/embedding/multimodal.py +308 -0
  60. maque/embedding/server.py +523 -0
  61. maque/embedding/text.py +311 -0
  62. maque/git/__init__.py +24 -0
  63. maque/git/pure_git.py +912 -0
  64. maque/io/__init__.py +29 -0
  65. maque/io/core.py +38 -0
  66. maque/io/ops.py +194 -0
  67. maque/llm/__init__.py +111 -0
  68. maque/llm/backend.py +416 -0
  69. maque/llm/base.py +411 -0
  70. maque/llm/server.py +366 -0
  71. maque/mcp_server.py +1096 -0
  72. maque/mllm_data_processor_pipeline/__init__.py +17 -0
  73. maque/mllm_data_processor_pipeline/core.py +341 -0
  74. maque/mllm_data_processor_pipeline/example.py +291 -0
  75. maque/mllm_data_processor_pipeline/steps/__init__.py +56 -0
  76. maque/mllm_data_processor_pipeline/steps/data_alignment.py +267 -0
  77. maque/mllm_data_processor_pipeline/steps/data_loader.py +172 -0
  78. maque/mllm_data_processor_pipeline/steps/data_validation.py +304 -0
  79. maque/mllm_data_processor_pipeline/steps/format_conversion.py +411 -0
  80. maque/mllm_data_processor_pipeline/steps/mllm_annotation.py +331 -0
  81. maque/mllm_data_processor_pipeline/steps/mllm_refinement.py +446 -0
  82. maque/mllm_data_processor_pipeline/steps/result_validation.py +501 -0
  83. maque/mllm_data_processor_pipeline/web_app.py +317 -0
  84. maque/nlp/__init__.py +14 -0
  85. maque/nlp/ngram.py +9 -0
  86. maque/nlp/parser.py +63 -0
  87. maque/nlp/risk_matcher.py +543 -0
  88. maque/nlp/sentence_splitter.py +202 -0
  89. maque/nlp/simple_tradition_cvt.py +31 -0
  90. maque/performance/__init__.py +21 -0
  91. maque/performance/_measure_time.py +70 -0
  92. maque/performance/_profiler.py +367 -0
  93. maque/performance/_stat_memory.py +51 -0
  94. maque/pipelines/__init__.py +15 -0
  95. maque/pipelines/clustering.py +252 -0
  96. maque/quantization/__init__.py +42 -0
  97. maque/quantization/auto_round.py +120 -0
  98. maque/quantization/base.py +145 -0
  99. maque/quantization/bitsandbytes.py +127 -0
  100. maque/quantization/llm_compressor.py +102 -0
  101. maque/retriever/__init__.py +35 -0
  102. maque/retriever/chroma.py +654 -0
  103. maque/retriever/document.py +140 -0
  104. maque/retriever/milvus.py +1140 -0
  105. maque/table_ops/__init__.py +1 -0
  106. maque/table_ops/core.py +133 -0
  107. maque/table_viewer/__init__.py +4 -0
  108. maque/table_viewer/download_assets.py +57 -0
  109. maque/table_viewer/server.py +698 -0
  110. maque/table_viewer/static/element-plus-icons.js +5791 -0
  111. maque/table_viewer/static/element-plus.css +1 -0
  112. maque/table_viewer/static/element-plus.js +65236 -0
  113. maque/table_viewer/static/main.css +268 -0
  114. maque/table_viewer/static/main.js +669 -0
  115. maque/table_viewer/static/vue.global.js +18227 -0
  116. maque/table_viewer/templates/index.html +401 -0
  117. maque/utils/__init__.py +56 -0
  118. maque/utils/color.py +68 -0
  119. maque/utils/color_string.py +45 -0
  120. maque/utils/compress.py +66 -0
  121. maque/utils/constant.py +183 -0
  122. maque/utils/core.py +261 -0
  123. maque/utils/cursor.py +143 -0
  124. maque/utils/distance.py +58 -0
  125. maque/utils/docker.py +96 -0
  126. maque/utils/downloads.py +51 -0
  127. maque/utils/excel_helper.py +542 -0
  128. maque/utils/helper_metrics.py +121 -0
  129. maque/utils/helper_parser.py +168 -0
  130. maque/utils/net.py +64 -0
  131. maque/utils/nvidia_stat.py +140 -0
  132. maque/utils/ops.py +53 -0
  133. maque/utils/packages.py +31 -0
  134. maque/utils/path.py +57 -0
  135. maque/utils/tar.py +260 -0
  136. maque/utils/untar.py +129 -0
  137. maque/web/__init__.py +0 -0
  138. maque/web/image_downloader.py +1410 -0
  139. maque-0.2.1.dist-info/METADATA +450 -0
  140. maque-0.2.1.dist-info/RECORD +143 -0
  141. maque-0.2.1.dist-info/WHEEL +4 -0
  142. maque-0.2.1.dist-info/entry_points.txt +3 -0
  143. maque-0.2.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,490 @@
1
+ """服务管理命令组"""
2
+ import os
3
+ import json
4
+ import subprocess
5
+ import time
6
+ import signal
7
+ import psutil
8
+ from pathlib import Path
9
+ from rich import print
10
+ from rich.table import Table
11
+ from rich.console import Console
12
+
13
+
14
+ class ServiceGroup:
15
+ """服务管理命令组"""
16
+
17
+ def __init__(self, cli_instance):
18
+ self.cli = cli_instance
19
+ self.console = Console()
20
+ self.services_config_file = Path.home() / '.maque' / 'services.json'
21
+
22
+ def _ensure_config_dir(self):
23
+ """确保配置目录存在"""
24
+ self.services_config_file.parent.mkdir(exist_ok=True)
25
+
26
+ def _load_services_config(self):
27
+ """加载服务配置"""
28
+ if self.services_config_file.exists():
29
+ with open(self.services_config_file, 'r', encoding='utf-8') as f:
30
+ return json.load(f)
31
+ return {}
32
+
33
+ def _save_services_config(self, config):
34
+ """保存服务配置"""
35
+ self._ensure_config_dir()
36
+ with open(self.services_config_file, 'w', encoding='utf-8') as f:
37
+ json.dump(config, f, indent=2, ensure_ascii=False)
38
+
39
+ def list(self, status: bool = True):
40
+ """列出已注册的服务
41
+
42
+ Args:
43
+ status: 是否显示服务状态,默认True
44
+ """
45
+ config = self._load_services_config()
46
+
47
+ if not config:
48
+ print("[yellow]没有已注册的服务[/yellow]")
49
+ print("使用 'maque service register' 注册服务")
50
+ return
51
+
52
+ print("[bold blue]已注册的服务[/bold blue]\n")
53
+
54
+ table = Table(show_header=True, header_style="bold magenta")
55
+ table.add_column("服务名", style="cyan")
56
+ table.add_column("端口", style="green")
57
+ table.add_column("命令", style="yellow")
58
+ if status:
59
+ table.add_column("状态", style="blue")
60
+ table.add_column("PID", style="dim")
61
+
62
+ for service_name, service_info in config.items():
63
+ port = str(service_info.get('port', 'N/A'))
64
+ command = service_info.get('command', 'N/A')
65
+
66
+ if status:
67
+ service_status, pid = self._get_service_status(service_info)
68
+ pid_str = str(pid) if pid else "N/A"
69
+
70
+ status_color = {
71
+ 'running': '[green]运行中[/green]',
72
+ 'stopped': '[red]已停止[/red]',
73
+ 'unknown': '[yellow]未知[/yellow]'
74
+ }.get(service_status, service_status)
75
+
76
+ table.add_row(service_name, port, command, status_color, pid_str)
77
+ else:
78
+ table.add_row(service_name, port, command)
79
+
80
+ self.console.print(table)
81
+
82
+ def register(
83
+ self,
84
+ name: str,
85
+ command: str,
86
+ port: int = None,
87
+ working_dir: str = None,
88
+ env_vars: str = None,
89
+ description: str = None
90
+ ):
91
+ """注册新服务
92
+
93
+ Args:
94
+ name: 服务名称
95
+ command: 启动命令
96
+ port: 服务端口(可选)
97
+ working_dir: 工作目录(可选)
98
+ env_vars: 环境变量,格式: "KEY1=VALUE1,KEY2=VALUE2"
99
+ description: 服务描述
100
+
101
+ Examples:
102
+ maque service register ollama "ollama serve" --port=11434
103
+ maque service register my-api "python app.py" --port=8000 --working_dir="/path/to/app"
104
+ """
105
+ config = self._load_services_config()
106
+
107
+ if name in config:
108
+ print(f"[yellow]服务 '{name}' 已存在,是否覆盖?[/yellow]")
109
+ response = input("(y/N): ")
110
+ if response.lower() != 'y':
111
+ print("操作已取消")
112
+ return
113
+
114
+ # 解析环境变量
115
+ env_dict = {}
116
+ if env_vars:
117
+ for pair in env_vars.split(','):
118
+ if '=' in pair:
119
+ key, value = pair.split('=', 1)
120
+ env_dict[key.strip()] = value.strip()
121
+
122
+ service_config = {
123
+ 'command': command,
124
+ 'working_dir': working_dir or os.getcwd(),
125
+ 'env_vars': env_dict,
126
+ 'description': description or '',
127
+ 'created_at': str(time.time())
128
+ }
129
+
130
+ if port:
131
+ service_config['port'] = port
132
+
133
+ config[name] = service_config
134
+ self._save_services_config(config)
135
+
136
+ print(f"[green]✓ 服务 '{name}' 已注册[/green]")
137
+
138
+ def unregister(self, name: str, force: bool = False):
139
+ """注销服务
140
+
141
+ Args:
142
+ name: 服务名称
143
+ force: 强制注销(不询问确认)
144
+ """
145
+ config = self._load_services_config()
146
+
147
+ if name not in config:
148
+ print(f"[red]服务 '{name}' 不存在[/red]")
149
+ return
150
+
151
+ # 检查服务是否在运行
152
+ status, pid = self._get_service_status(config[name])
153
+ if status == 'running':
154
+ print(f"[yellow]警告: 服务 '{name}' 正在运行 (PID: {pid})[/yellow]")
155
+ if not force:
156
+ response = input("是否先停止服务并注销?(y/N): ")
157
+ if response.lower() != 'y':
158
+ print("操作已取消")
159
+ return
160
+ self.stop(name)
161
+
162
+ del config[name]
163
+ self._save_services_config(config)
164
+ print(f"[green]✓ 服务 '{name}' 已注销[/green]")
165
+
166
+ def start(self, name: str, detach: bool = True):
167
+ """启动服务
168
+
169
+ Args:
170
+ name: 服务名称
171
+ detach: 是否后台运行,默认True
172
+ """
173
+ config = self._load_services_config()
174
+
175
+ if name not in config:
176
+ print(f"[red]服务 '{name}' 不存在[/red]")
177
+ print("使用 'maque service list' 查看已注册的服务")
178
+ return
179
+
180
+ service_info = config[name]
181
+ status, pid = self._get_service_status(service_info)
182
+
183
+ if status == 'running':
184
+ print(f"[yellow]服务 '{name}' 已在运行 (PID: {pid})[/yellow]")
185
+ return
186
+
187
+ print(f"[blue]启动服务 '{name}'...[/blue]")
188
+
189
+ # 准备环境
190
+ env = os.environ.copy()
191
+ env.update(service_info.get('env_vars', {}))
192
+
193
+ working_dir = service_info.get('working_dir', os.getcwd())
194
+ command = service_info['command']
195
+
196
+ try:
197
+ if detach:
198
+ # 后台启动
199
+ process = subprocess.Popen(
200
+ command,
201
+ shell=True,
202
+ cwd=working_dir,
203
+ env=env,
204
+ stdout=subprocess.DEVNULL,
205
+ stderr=subprocess.DEVNULL,
206
+ start_new_session=True
207
+ )
208
+
209
+ # 等待一小段时间确认启动
210
+ time.sleep(2)
211
+ if process.poll() is None:
212
+ # 保存PID信息
213
+ config[name]['pid'] = process.pid
214
+ config[name]['last_started'] = str(time.time())
215
+ self._save_services_config(config)
216
+
217
+ print(f"[green]✓ 服务 '{name}' 已启动 (PID: {process.pid})[/green]")
218
+ else:
219
+ print(f"[red]✗ 服务 '{name}' 启动失败[/red]")
220
+ else:
221
+ # 前台启动
222
+ print(f"[blue]在前台运行服务 '{name}'...[/blue]")
223
+ print(f"命令: {command}")
224
+ print(f"工作目录: {working_dir}")
225
+ print("按 Ctrl+C 停止服务\n")
226
+
227
+ subprocess.run(command, shell=True, cwd=working_dir, env=env)
228
+
229
+ except KeyboardInterrupt:
230
+ print(f"\n[yellow]服务 '{name}' 已中断[/yellow]")
231
+ except Exception as e:
232
+ print(f"[red]启动服务失败: {e}[/red]")
233
+
234
+ def stop(self, name: str, timeout: int = 10):
235
+ """停止服务
236
+
237
+ Args:
238
+ name: 服务名称
239
+ timeout: 超时时间(秒),默认10秒
240
+ """
241
+ config = self._load_services_config()
242
+
243
+ if name not in config:
244
+ print(f"[red]服务 '{name}' 不存在[/red]")
245
+ return
246
+
247
+ service_info = config[name]
248
+ status, pid = self._get_service_status(service_info)
249
+
250
+ if status != 'running':
251
+ print(f"[yellow]服务 '{name}' 未在运行[/yellow]")
252
+ return
253
+
254
+ print(f"[blue]停止服务 '{name}' (PID: {pid})...[/blue]")
255
+
256
+ try:
257
+ process = psutil.Process(pid)
258
+
259
+ # 优雅停止
260
+ process.terminate()
261
+
262
+ # 等待进程结束
263
+ try:
264
+ process.wait(timeout=timeout)
265
+ print(f"[green]✓ 服务 '{name}' 已停止[/green]")
266
+ except psutil.TimeoutExpired:
267
+ # 强制终止
268
+ print(f"[yellow]服务超时,强制终止...[/yellow]")
269
+ process.kill()
270
+ process.wait(timeout=5)
271
+ print(f"[green]✓ 服务 '{name}' 已强制停止[/green]")
272
+
273
+ # 清理PID信息
274
+ if 'pid' in config[name]:
275
+ del config[name]['pid']
276
+ config[name]['last_stopped'] = str(time.time())
277
+ self._save_services_config(config)
278
+
279
+ except psutil.NoSuchProcess:
280
+ print(f"[yellow]进程不存在,清理服务状态[/yellow]")
281
+ if 'pid' in config[name]:
282
+ del config[name]['pid']
283
+ self._save_services_config(config)
284
+ except Exception as e:
285
+ print(f"[red]停止服务失败: {e}[/red]")
286
+
287
+ def restart(self, name: str):
288
+ """重启服务
289
+
290
+ Args:
291
+ name: 服务名称
292
+ """
293
+ print(f"[blue]重启服务 '{name}'...[/blue]")
294
+ self.stop(name)
295
+ time.sleep(1)
296
+ self.start(name)
297
+
298
+ def status(self, name: str = None):
299
+ """显示服务状态
300
+
301
+ Args:
302
+ name: 服务名称,不指定则显示所有服务状态
303
+ """
304
+ config = self._load_services_config()
305
+
306
+ if not config:
307
+ print("[yellow]没有已注册的服务[/yellow]")
308
+ return
309
+
310
+ if name:
311
+ if name not in config:
312
+ print(f"[red]服务 '{name}' 不存在[/red]")
313
+ return
314
+
315
+ self._show_service_detail(name, config[name])
316
+ else:
317
+ # 显示所有服务状态
318
+ self.list(status=True)
319
+
320
+ def _show_service_detail(self, name, service_info):
321
+ """显示单个服务的详细信息"""
322
+ status, pid = self._get_service_status(service_info)
323
+
324
+ print(f"[bold blue]服务详情: {name}[/bold blue]\n")
325
+
326
+ detail_table = Table(show_header=False, box=None)
327
+ detail_table.add_column("属性", style="cyan")
328
+ detail_table.add_column("值", style="green")
329
+
330
+ detail_table.add_row("状态", {
331
+ 'running': '[green]运行中[/green]',
332
+ 'stopped': '[red]已停止[/red]',
333
+ 'unknown': '[yellow]未知[/yellow]'
334
+ }.get(status, status))
335
+
336
+ if pid:
337
+ detail_table.add_row("PID", str(pid))
338
+
339
+ detail_table.add_row("命令", service_info.get('command', 'N/A'))
340
+ detail_table.add_row("工作目录", service_info.get('working_dir', 'N/A'))
341
+
342
+ if service_info.get('port'):
343
+ detail_table.add_row("端口", str(service_info['port']))
344
+
345
+ if service_info.get('description'):
346
+ detail_table.add_row("描述", service_info['description'])
347
+
348
+ # 环境变量
349
+ env_vars = service_info.get('env_vars', {})
350
+ if env_vars:
351
+ env_str = ", ".join([f"{k}={v}" for k, v in env_vars.items()])
352
+ detail_table.add_row("环境变量", env_str)
353
+
354
+ # 时间信息
355
+ if service_info.get('last_started'):
356
+ start_time = time.strftime('%Y-%m-%d %H:%M:%S',
357
+ time.localtime(float(service_info['last_started'])))
358
+ detail_table.add_row("最后启动", start_time)
359
+
360
+ if service_info.get('last_stopped'):
361
+ stop_time = time.strftime('%Y-%m-%d %H:%M:%S',
362
+ time.localtime(float(service_info['last_stopped'])))
363
+ detail_table.add_row("最后停止", stop_time)
364
+
365
+ self.console.print(detail_table)
366
+
367
+ def _get_service_status(self, service_info):
368
+ """获取服务状态"""
369
+ pid = service_info.get('pid')
370
+
371
+ if not pid:
372
+ return 'stopped', None
373
+
374
+ try:
375
+ process = psutil.Process(pid)
376
+ if process.is_running():
377
+ return 'running', pid
378
+ else:
379
+ return 'stopped', None
380
+ except psutil.NoSuchProcess:
381
+ return 'stopped', None
382
+ except Exception:
383
+ return 'unknown', pid
384
+
385
+ def logs(self, name: str, lines: int = 50, follow: bool = False):
386
+ """查看服务日志
387
+
388
+ Args:
389
+ name: 服务名称
390
+ lines: 显示行数,默认50行
391
+ follow: 是否持续跟踪日志,默认False
392
+ """
393
+ config = self._load_services_config()
394
+
395
+ if name not in config:
396
+ print(f"[red]服务 '{name}' 不存在[/red]")
397
+ return
398
+
399
+ service_info = config[name]
400
+ log_file = Path.home() / '.maque' / 'logs' / f'{name}.log'
401
+
402
+ if not log_file.exists():
403
+ print(f"[yellow]服务 '{name}' 的日志文件不存在[/yellow]")
404
+ print(f"预期位置: {log_file}")
405
+ return
406
+
407
+ try:
408
+ if follow:
409
+ print(f"[blue]跟踪服务 '{name}' 的日志 (按 Ctrl+C 停止)[/blue]\n")
410
+ # 简单的tail -f实现
411
+ import subprocess
412
+ subprocess.run(['tail', '-f', str(log_file)])
413
+ else:
414
+ print(f"[blue]服务 '{name}' 的最近 {lines} 行日志[/blue]\n")
415
+ with open(log_file, 'r', encoding='utf-8') as f:
416
+ all_lines = f.readlines()
417
+ recent_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
418
+
419
+ for line in recent_lines:
420
+ print(line.rstrip())
421
+
422
+ except KeyboardInterrupt:
423
+ print(f"\n[yellow]停止跟踪日志[/yellow]")
424
+ except Exception as e:
425
+ print(f"[red]读取日志失败: {e}[/red]")
426
+
427
+ def health(self, name: str = None):
428
+ """检查服务健康状态
429
+
430
+ Args:
431
+ name: 服务名称,不指定则检查所有服务
432
+ """
433
+ config = self._load_services_config()
434
+
435
+ if not config:
436
+ print("[yellow]没有已注册的服务[/yellow]")
437
+ return
438
+
439
+ services_to_check = [name] if name else list(config.keys())
440
+
441
+ print("[bold blue]服务健康检查[/bold blue]\n")
442
+
443
+ health_table = Table(show_header=True, header_style="bold magenta")
444
+ health_table.add_column("服务名", style="cyan")
445
+ health_table.add_column("状态", style="green")
446
+ health_table.add_column("端口检查", style="yellow")
447
+ health_table.add_column("响应时间", style="blue")
448
+
449
+ for service_name in services_to_check:
450
+ if service_name not in config:
451
+ continue
452
+
453
+ service_info = config[service_name]
454
+ status, pid = self._get_service_status(service_info)
455
+
456
+ # 端口检查
457
+ port_status = "N/A"
458
+ response_time = "N/A"
459
+
460
+ if service_info.get('port'):
461
+ port_check, resp_time = self._check_port_health(service_info['port'])
462
+ port_status = "[green]正常[/green]" if port_check else "[red]异常[/red]"
463
+ response_time = f"{resp_time:.2f}ms" if resp_time else "N/A"
464
+
465
+ status_display = {
466
+ 'running': '[green]运行中[/green]',
467
+ 'stopped': '[red]已停止[/red]',
468
+ 'unknown': '[yellow]未知[/yellow]'
469
+ }.get(status, status)
470
+
471
+ health_table.add_row(service_name, status_display, port_status, response_time)
472
+
473
+ self.console.print(health_table)
474
+
475
+ def _check_port_health(self, port):
476
+ """检查端口健康状态"""
477
+ import socket
478
+ import time
479
+
480
+ try:
481
+ start_time = time.time()
482
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
483
+ sock.settimeout(5)
484
+ result = sock.connect_ex(('localhost', port))
485
+ sock.close()
486
+
487
+ response_time = (time.time() - start_time) * 1000
488
+ return result == 0, response_time
489
+ except Exception:
490
+ return False, None