gpu-worker 1.0.0 → 1.0.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/README.md CHANGED
@@ -33,6 +33,23 @@ gpu-worker configure
33
33
  gpu-worker start
34
34
  ```
35
35
 
36
+ ### Using system Python / skip install
37
+
38
+ If your server already has GPU dependencies installed globally, you can skip the virtual environment and auto-install:
39
+
40
+ ```bash
41
+ npx gpu-worker configure --use-system-python
42
+ npx gpu-worker start --use-system-python
43
+ ```
44
+
45
+ You can also keep a venv but skip dependency installation:
46
+
47
+ ```bash
48
+ npx gpu-worker configure --skip-install
49
+ ```
50
+
51
+ On first run without a venv, the interactive CLI will prompt you to choose the mode.
52
+
36
53
  ## Requirements
37
54
 
38
55
  - **Node.js**: >= 16.0.0
@@ -41,6 +58,9 @@ gpu-worker start
41
58
  - **RAM**: 16GB+ recommended
42
59
  - **Storage**: 50GB+ for model storage
43
60
 
61
+ CUDA note: the installer uses `nvidia-smi` to detect CUDA and selects `cu124` / `cu121` / `cu118` for PyTorch.
62
+ CUDA 12.2/12.3 use `cu121`. Other versions fall back to CPU builds.
63
+
44
64
  ## Configuration
45
65
 
46
66
  The worker can be configured via:
package/bin/gpu-worker.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  /**
3
3
  * GPU Worker CLI - Node.js 入口
4
4
  * 包装 Python Worker,提供简单的 npm/npx 安装体验
@@ -38,8 +38,8 @@ function findPython() {
38
38
  }
39
39
 
40
40
  // 检查虚拟环境
41
- function getVenvPython() {
42
- const venvPath = path.join(PACKAGE_DIR, '.venv');
41
+ function getVenvPython() {
42
+ const venvPath = path.join(PACKAGE_DIR, '.venv');
43
43
 
44
44
  if (process.platform === 'win32') {
45
45
  const pythonPath = path.join(venvPath, 'Scripts', 'python.exe');
@@ -48,12 +48,46 @@ function getVenvPython() {
48
48
  const pythonPath = path.join(venvPath, 'bin', 'python');
49
49
  if (fs.existsSync(pythonPath)) return pythonPath;
50
50
  }
51
- return null;
52
- }
53
-
54
- // 创建虚拟环境
55
- async function createVenv(pythonCmd) {
56
- const spinner = ora('Creating Python virtual environment...').start();
51
+ return null;
52
+ }
53
+
54
+ function detectCudaVersion() {
55
+ try {
56
+ const output = execSync('nvidia-smi', { encoding: 'utf8' });
57
+ const match = output.match(/CUDA Version:\\s*(\\d+)\\.(\\d+)/);
58
+ if (!match) return null;
59
+ return { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) };
60
+ } catch (e) {
61
+ return null;
62
+ }
63
+ }
64
+
65
+ function selectTorchIndexUrl(cudaVersion) {
66
+ if (!cudaVersion) return null;
67
+ const versionValue = cudaVersion.major * 100 + cudaVersion.minor;
68
+ if (versionValue >= 1204) return 'https://download.pytorch.org/whl/cu124';
69
+ if (versionValue >= 1201) return 'https://download.pytorch.org/whl/cu121';
70
+ if (versionValue >= 1108) return 'https://download.pytorch.org/whl/cu118';
71
+ return null;
72
+ }
73
+
74
+ function createRequirementsWithoutTorch(requirementsFile) {
75
+ const content = fs.readFileSync(requirementsFile, 'utf8');
76
+ const filtered = content
77
+ .split(/\r?\n/)
78
+ .filter((line) => {
79
+ const trimmed = line.trim();
80
+ if (!trimmed || trimmed.startsWith('#')) return true;
81
+ return !/^torch([<>=!~].*)?$/.test(trimmed);
82
+ });
83
+ const filteredPath = path.join(PACKAGE_DIR, '.requirements.no-torch.txt');
84
+ fs.writeFileSync(filteredPath, filtered.join('\n'));
85
+ return filteredPath;
86
+ }
87
+
88
+ // 创建虚拟环境
89
+ async function createVenv(pythonCmd) {
90
+ const spinner = ora('Creating Python virtual environment...').start();
57
91
  const venvPath = path.join(PACKAGE_DIR, '.venv');
58
92
 
59
93
  try {
@@ -68,24 +102,49 @@ async function createVenv(pythonCmd) {
68
102
  }
69
103
 
70
104
  // 安装 Python 依赖
71
- async function installDependencies() {
72
- const venvPython = getVenvPython();
73
- if (!venvPython) {
74
- console.error(chalk.red('Virtual environment not found'));
75
- return false;
76
- }
77
-
78
- const spinner = ora('Installing Python dependencies...').start();
79
- const requirementsFile = path.join(PACKAGE_DIR, 'requirements.txt');
80
-
81
- try {
82
- execSync(`"${venvPython}" -m pip install -r "${requirementsFile}" -q`, {
83
- stdio: 'pipe',
84
- timeout: 600000 // 10分钟超时
85
- });
86
- spinner.succeed('Dependencies installed');
87
- return true;
88
- } catch (e) {
105
+ async function installDependencies() {
106
+ const venvPython = getVenvPython();
107
+ if (!venvPython) {
108
+ console.error(chalk.red('Virtual environment not found'));
109
+ return false;
110
+ }
111
+
112
+ const spinner = ora('Installing Python dependencies...').start();
113
+ const requirementsFile = path.join(PACKAGE_DIR, 'requirements.txt');
114
+ const cudaVersion = detectCudaVersion();
115
+ const torchIndexUrl = selectTorchIndexUrl(cudaVersion);
116
+
117
+ try {
118
+ if (cudaVersion && !torchIndexUrl) {
119
+ console.log(chalk.yellow(`检测到 CUDA ${cudaVersion.major}.${cudaVersion.minor},无匹配的 PyTorch 版本,将安装 CPU 版。`));
120
+ }
121
+ if (torchIndexUrl) {
122
+ execSync(`"${venvPython}" -m pip install torch --index-url "${torchIndexUrl}" --upgrade --force-reinstall -q`, {
123
+ stdio: 'pipe',
124
+ timeout: 600000 // 10分钟超时
125
+ });
126
+ } else {
127
+ execSync(`"${venvPython}" -m pip install "torch>=2.0.0" -q`, {
128
+ stdio: 'pipe',
129
+ timeout: 600000 // 10分钟超时
130
+ });
131
+ }
132
+
133
+ const filteredRequirements = createRequirementsWithoutTorch(requirementsFile);
134
+ try {
135
+ execSync(`"${venvPython}" -m pip install -r "${filteredRequirements}" -q`, {
136
+ stdio: 'pipe',
137
+ timeout: 600000 // 10分钟超时
138
+ });
139
+ } finally {
140
+ if (fs.existsSync(filteredRequirements)) {
141
+ fs.unlinkSync(filteredRequirements);
142
+ }
143
+ }
144
+
145
+ spinner.succeed('Dependencies installed');
146
+ return true;
147
+ } catch (e) {
89
148
  spinner.fail('Failed to install dependencies');
90
149
  console.error(chalk.red(e.message));
91
150
  return false;
@@ -93,24 +152,24 @@ async function installDependencies() {
93
152
  }
94
153
 
95
154
  // 运行 Python CLI
96
- function runPythonCLI(args) {
97
- let pythonCmd = getVenvPython() || findPython();
98
-
99
- if (!pythonCmd) {
100
- console.error(chalk.red('Python 3.9+ not found!'));
101
- console.log(chalk.yellow('Please install Python 3.9 or higher:'));
102
- console.log(' - Windows: https://www.python.org/downloads/');
103
- console.log(' - macOS: brew install python@3.11');
104
- console.log(' - Linux: sudo apt install python3.11');
105
- process.exit(1);
106
- }
107
-
108
- const cliPath = path.join(PACKAGE_DIR, 'cli.py');
109
-
110
- const proc = spawn(pythonCmd, [cliPath, ...args], {
111
- stdio: 'inherit',
112
- cwd: process.cwd()
113
- });
155
+ function runPythonCLI(args, pythonCmd) {
156
+ const resolvedCmd = pythonCmd || getVenvPython() || findPython();
157
+
158
+ if (!resolvedCmd) {
159
+ console.error(chalk.red('Python 3.9+ not found!'));
160
+ console.log(chalk.yellow('Please install Python 3.9 or higher:'));
161
+ console.log(' - Windows: https://www.python.org/downloads/');
162
+ console.log(' - macOS: brew install python@3.11');
163
+ console.log(' - Linux: sudo apt install python3.11');
164
+ process.exit(1);
165
+ }
166
+
167
+ const cliPath = path.join(PACKAGE_DIR, 'cli.py');
168
+
169
+ const proc = spawn(resolvedCmd, [cliPath, ...args], {
170
+ stdio: 'inherit',
171
+ cwd: process.cwd()
172
+ });
114
173
 
115
174
  proc.on('close', (code) => {
116
175
  process.exit(code);
@@ -123,99 +182,195 @@ function runPythonCLI(args) {
123
182
  }
124
183
 
125
184
  // 初始化检查
126
- async function ensureSetup() {
127
- const venvPython = getVenvPython();
128
-
129
- if (!venvPython) {
130
- console.log(chalk.cyan('First time setup detected. Setting up environment...\n'));
131
-
132
- const pythonCmd = findPython();
133
- if (!pythonCmd) {
134
- console.error(chalk.red('Python 3.9+ is required but not found!'));
135
- console.log(chalk.yellow('\nPlease install Python:'));
136
- console.log(' - Windows: https://www.python.org/downloads/');
137
- console.log(' - macOS: brew install python@3.11');
138
- console.log(' - Linux: sudo apt install python3.11');
139
- process.exit(1);
140
- }
141
-
142
- console.log(chalk.green(`Found Python: ${pythonCmd}`));
143
-
144
- if (!await createVenv(pythonCmd)) {
145
- process.exit(1);
146
- }
147
-
148
- if (!await installDependencies()) {
149
- process.exit(1);
150
- }
151
-
152
- console.log(chalk.green('\n✓ Setup complete!\n'));
153
- }
154
- }
155
185
 
156
186
  // 主程序
157
- const program = new Command();
158
-
159
- program
160
- .name('gpu-worker')
161
- .description('分布式GPU推理 Worker 节点')
162
- .version('1.0.0');
163
-
164
- program
165
- .command('install')
166
- .description('安装/更新 Python 依赖')
167
- .action(async () => {
168
- await ensureSetup();
169
- await installDependencies();
170
- });
171
-
172
- program
173
- .command('configure')
174
- .description('交互式配置向导')
175
- .action(async () => {
176
- await ensureSetup();
177
- runPythonCLI(['configure']);
178
- });
179
-
180
- program
181
- .command('start')
182
- .description('启动 Worker')
183
- .option('-c, --config <path>', '配置文件路径', 'config.yaml')
184
- .action(async (options) => {
185
- await ensureSetup();
187
+ async function ensureSetup(options = {}) {
188
+ const allowPrompt = Boolean(options.allowPrompt);
189
+ let useSystemPython = Boolean(options.useSystemPython);
190
+ let skipInstall = Boolean(options.skipInstall);
191
+
192
+ if (useSystemPython) {
193
+ const pythonCmd = findPython();
194
+ if (!pythonCmd) {
195
+ console.error(chalk.red('Python 3.9+ is required but not found!'));
196
+ console.log(chalk.yellow('\nPlease install Python:'));
197
+ console.log(' - Windows: https://www.python.org/downloads/');
198
+ console.log(' - macOS: brew install python@3.11');
199
+ console.log(' - Linux: sudo apt install python3.11');
200
+ process.exit(1);
201
+ }
202
+
203
+ console.log(chalk.green(`Using system Python: ${pythonCmd}`));
204
+ return { pythonCmd, useSystemPython: true };
205
+ }
206
+
207
+ let venvPython = getVenvPython();
208
+ let createdVenv = false;
209
+
210
+ if (!venvPython) {
211
+ if (allowPrompt) {
212
+ const { mode } = await inquirer.prompt([{
213
+ type: 'list',
214
+ name: 'mode',
215
+ message: '未检测到虚拟环境,请选择运行方式:',
216
+ choices: [
217
+ { name: '使用系统 Python(跳过虚拟环境与依赖安装)', value: 'system' },
218
+ { name: '创建虚拟环境并安装依赖(推荐)', value: 'venv' },
219
+ { name: '创建虚拟环境但跳过依赖安装', value: 'venv-skip-install' }
220
+ ]
221
+ }]);
222
+
223
+ if (mode === 'system') {
224
+ useSystemPython = true;
225
+ const pythonCmd = findPython();
226
+ if (!pythonCmd) {
227
+ console.error(chalk.red('Python 3.9+ is required but not found!'));
228
+ console.log(chalk.yellow('\nPlease install Python:'));
229
+ console.log(' - Windows: https://www.python.org/downloads/');
230
+ console.log(' - macOS: brew install python@3.11');
231
+ console.log(' - Linux: sudo apt install python3.11');
232
+ process.exit(1);
233
+ }
234
+
235
+ console.log(chalk.green(`Using system Python: ${pythonCmd}`));
236
+ return { pythonCmd, useSystemPython: true };
237
+ }
238
+
239
+ if (mode === 'venv-skip-install') {
240
+ skipInstall = true;
241
+ }
242
+ }
243
+
244
+ console.log(chalk.cyan('First time setup detected. Setting up environment...\n'));
245
+
246
+ const pythonCmd = findPython();
247
+ if (!pythonCmd) {
248
+ console.error(chalk.red('Python 3.9+ is required but not found!'));
249
+ console.log(chalk.yellow('\nPlease install Python:'));
250
+ console.log(' - Windows: https://www.python.org/downloads/');
251
+ console.log(' - macOS: brew install python@3.11');
252
+ console.log(' - Linux: sudo apt install python3.11');
253
+ process.exit(1);
254
+ }
255
+
256
+ console.log(chalk.green(`Found Python: ${pythonCmd}`));
257
+
258
+ if (!await createVenv(pythonCmd)) {
259
+ process.exit(1);
260
+ }
261
+
262
+ createdVenv = true;
263
+ venvPython = getVenvPython();
264
+ }
265
+
266
+ if (createdVenv && !skipInstall) {
267
+ if (!await installDependencies()) {
268
+ process.exit(1);
269
+ }
270
+
271
+ console.log(chalk.green('\nSetup complete!\n'));
272
+ }
273
+
274
+ return { pythonCmd: venvPython || findPython(), useSystemPython: false };
275
+ }
276
+
277
+ function addCommonOptions(command) {
278
+ return command
279
+ .option('--use-system-python', '使用系统 Python(跳过虚拟环境与依赖安装)')
280
+ .option('--skip-install', '跳过依赖安装,仅使用已有环境');
281
+ }
282
+
283
+ const program = new Command();
284
+
285
+ program
286
+ .name('gpu-worker')
287
+ .description('分布式GPU推理 Worker 节点')
288
+ .version('1.0.0')
289
+ .option('--use-system-python', '使用系统 Python(跳过虚拟环境与依赖安装)')
290
+ .option('--skip-install', '跳过依赖安装,仅使用已有环境');
291
+
292
+ addCommonOptions(
293
+ program
294
+ .command('install')
295
+ )
296
+ .description('安装/更新 Python 依赖')
297
+ .action(async function () {
298
+ const opts = { ...program.opts(), ...this.opts() };
299
+ if (opts.useSystemPython) {
300
+ console.log(chalk.yellow('install 仅针对虚拟环境,系统 Python 请自行安装依赖。'));
301
+ return;
302
+ }
303
+ await ensureSetup({ ...opts, useSystemPython: false, skipInstall: true, allowPrompt: false });
304
+ await installDependencies();
305
+ });
306
+
307
+ addCommonOptions(
308
+ program
309
+ .command('configure')
310
+ )
311
+ .description('交互式配置向导')
312
+ .action(async function () {
313
+ const opts = { ...program.opts(), ...this.opts() };
314
+ const setup = await ensureSetup({ ...opts, allowPrompt: false });
315
+ runPythonCLI(['configure'], setup.pythonCmd);
316
+ });
317
+
318
+ addCommonOptions(
319
+ program
320
+ .command('start')
321
+ )
322
+ .description('启动 Worker')
323
+ .option('-c, --config <path>', '配置文件路径', 'config.yaml')
324
+ .action(async function () {
325
+ const cmdOpts = this.opts();
326
+ const opts = { ...program.opts(), ...cmdOpts };
327
+ const setup = await ensureSetup({ ...opts, allowPrompt: false });
186
328
 
187
329
  // 检查配置文件
188
- const configPath = path.resolve(options.config);
330
+ const configPath = path.resolve(cmdOpts.config);
189
331
  if (!fs.existsSync(configPath)) {
190
332
  console.log(chalk.yellow('No config file found. Starting configuration wizard...\n'));
191
- runPythonCLI(['configure']);
333
+ runPythonCLI(['configure'], setup.pythonCmd);
192
334
  return;
193
335
  }
194
336
 
195
- runPythonCLI(['start', '-c', configPath]);
196
- });
197
-
198
- program
199
- .command('status')
200
- .description('查看状态')
201
- .action(async () => {
202
- await ensureSetup();
203
- runPythonCLI(['status']);
204
- });
205
-
206
- program
207
- .command('set <key> <value>')
208
- .description('设置配置项')
209
- .action(async (key, value) => {
210
- await ensureSetup();
211
- runPythonCLI(['set', key, value]);
337
+ runPythonCLI(['start', '-c', configPath], setup.pythonCmd);
212
338
  });
213
339
 
214
- program
215
- .command('setup')
216
- .description('初始化环境(创建虚拟环境并安装依赖)')
217
- .action(async () => {
218
- const pythonCmd = findPython();
340
+ addCommonOptions(
341
+ program
342
+ .command('status')
343
+ )
344
+ .description('查看状态')
345
+ .action(async function () {
346
+ const opts = { ...program.opts(), ...this.opts() };
347
+ const setup = await ensureSetup({ ...opts, allowPrompt: false });
348
+ runPythonCLI(['status'], setup.pythonCmd);
349
+ });
350
+
351
+ addCommonOptions(
352
+ program
353
+ .command('set <key> <value>')
354
+ )
355
+ .description('设置配置项')
356
+ .action(async function (key, value) {
357
+ const opts = { ...program.opts(), ...this.opts() };
358
+ const setup = await ensureSetup({ ...opts, allowPrompt: false });
359
+ runPythonCLI(['set', key, value], setup.pythonCmd);
360
+ });
361
+
362
+ addCommonOptions(
363
+ program
364
+ .command('setup')
365
+ )
366
+ .description('初始化环境(创建虚拟环境并安装依赖)')
367
+ .action(async function () {
368
+ const opts = { ...program.opts(), ...this.opts() };
369
+ if (opts.useSystemPython) {
370
+ console.log(chalk.yellow('setup 仅用于虚拟环境,系统 Python 请自行安装依赖。'));
371
+ return;
372
+ }
373
+ const pythonCmd = findPython();
219
374
  if (!pythonCmd) {
220
375
  console.error(chalk.red('Python 3.9+ not found!'));
221
376
  process.exit(1);
@@ -224,8 +379,10 @@ program
224
379
  console.log(chalk.cyan('Setting up GPU Worker environment...\n'));
225
380
  console.log(chalk.green(`Python: ${pythonCmd}`));
226
381
 
227
- await createVenv(pythonCmd);
228
- await installDependencies();
382
+ await createVenv(pythonCmd);
383
+ if (!opts.skipInstall) {
384
+ await installDependencies();
385
+ }
229
386
 
230
387
  console.log(chalk.green('\n✓ Setup complete!'));
231
388
  console.log(chalk.cyan('\nNext steps:'));
@@ -234,10 +391,13 @@ program
234
391
  });
235
392
 
236
393
  // 快速启动命令 (无参数时的默认行为)
237
- program
238
- .command('quick', { isDefault: true, hidden: true })
239
- .action(async () => {
240
- await ensureSetup();
394
+ addCommonOptions(
395
+ program
396
+ .command('quick', { isDefault: true, hidden: true })
397
+ )
398
+ .action(async function () {
399
+ const opts = { ...program.opts(), ...this.opts() };
400
+ const setup = await ensureSetup({ ...opts, allowPrompt: true });
241
401
 
242
402
  console.log(chalk.cyan.bold('\n GPU Worker - 分布式GPU推理节点\n'));
243
403
 
@@ -264,12 +424,12 @@ program
264
424
  const configPath = path.join(process.cwd(), 'config.yaml');
265
425
  if (!fs.existsSync(configPath)) {
266
426
  console.log(chalk.yellow('\n未找到配置文件,先进行配置...\n'));
267
- runPythonCLI(['configure']);
427
+ runPythonCLI(['configure'], setup.pythonCmd);
268
428
  return;
269
429
  }
270
430
  }
271
431
 
272
- runPythonCLI([action]);
432
+ runPythonCLI([action], setup.pythonCmd);
273
433
  });
274
434
 
275
435
  program.parse();
package/cli.py CHANGED
@@ -5,11 +5,12 @@ GPU Worker CLI 安装器和配置向导
5
5
  """
6
6
  import os
7
7
  import sys
8
- import argparse
9
- import subprocess
10
- import platform
11
- import shutil
12
- from pathlib import Path
8
+ import argparse
9
+ import subprocess
10
+ import platform
11
+ import shutil
12
+ import re
13
+ from pathlib import Path
13
14
  from typing import Optional, Dict, Any, List
14
15
  import json
15
16
  import time
@@ -73,13 +74,91 @@ def clear_screen():
73
74
  os.system('cls' if platform.system() == 'Windows' else 'clear')
74
75
 
75
76
 
76
- def check_gpu():
77
- """检测GPU信息"""
77
+ def _probe_nvidia_smi() -> Optional[Dict[str, Any]]:
78
+ """通过 nvidia-smi 获取 GPU 信息(无 PyTorch 时的兜底)"""
79
+ try:
80
+ result = subprocess.run(
81
+ ["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader,nounits"],
82
+ capture_output=True,
83
+ text=True
84
+ )
85
+ if result.returncode != 0:
86
+ return None
87
+
88
+ lines = [line.strip() for line in result.stdout.splitlines() if line.strip()]
89
+ if not lines:
90
+ return None
91
+
92
+ first = [part.strip() for part in lines[0].split(",")]
93
+ model = first[0] if first else "Unknown"
94
+ memory_gb = 0
95
+ if len(first) > 1:
96
+ try:
97
+ memory_gb = round(float(first[1]) / 1024, 1)
98
+ except ValueError:
99
+ memory_gb = 0
100
+
101
+ return {
102
+ "count": len(lines),
103
+ "model": model,
104
+ "memory_gb": memory_gb
105
+ }
106
+ except Exception:
107
+ return None
108
+
109
+
110
+ def _detect_cuda_version() -> Optional[Dict[str, int]]:
111
+ """从 nvidia-smi 输出中解析 CUDA 版本"""
112
+ try:
113
+ result = subprocess.run(
114
+ ["nvidia-smi"],
115
+ capture_output=True,
116
+ text=True
117
+ )
118
+ if result.returncode != 0:
119
+ return None
120
+
121
+ match = re.search(r"CUDA Version:\\s*(\\d+)\\.(\\d+)", result.stdout)
122
+ if not match:
123
+ return None
124
+
125
+ return {
126
+ "major": int(match.group(1)),
127
+ "minor": int(match.group(2))
128
+ }
129
+ except Exception:
130
+ return None
131
+
132
+
133
+ def _select_torch_index_url(cuda_version: Optional[Dict[str, int]]) -> Optional[str]:
134
+ """根据 CUDA 版本选择 PyTorch 安装源"""
135
+ if not cuda_version:
136
+ return None
137
+
138
+ major = cuda_version["major"]
139
+ minor = cuda_version["minor"]
140
+ version_value = major * 100 + minor
141
+
142
+ if version_value >= 1204:
143
+ return "https://download.pytorch.org/whl/cu124"
144
+ if version_value >= 1201:
145
+ return "https://download.pytorch.org/whl/cu121"
146
+ if version_value >= 1108:
147
+ return "https://download.pytorch.org/whl/cu118"
148
+ return None
149
+
150
+
151
+ def check_gpu():
152
+ """检测GPU信息"""
78
153
  gpu_info = {
79
154
  "available": False,
80
155
  "count": 0,
81
156
  "model": "Unknown",
82
- "memory_gb": 0
157
+ "memory_gb": 0,
158
+ "nvidia_detected": False,
159
+ "nvidia_model": None,
160
+ "nvidia_memory_gb": None,
161
+ "nvidia_count": 0
83
162
  }
84
163
 
85
164
  try:
@@ -93,14 +172,22 @@ def check_gpu():
93
172
  except ImportError:
94
173
  pass
95
174
 
175
+ if not gpu_info["available"]:
176
+ nvidia_info = _probe_nvidia_smi()
177
+ if nvidia_info:
178
+ gpu_info["nvidia_detected"] = True
179
+ gpu_info["nvidia_count"] = nvidia_info["count"]
180
+ gpu_info["nvidia_model"] = nvidia_info["model"]
181
+ gpu_info["nvidia_memory_gb"] = nvidia_info["memory_gb"]
182
+
96
183
  return gpu_info
97
184
 
98
185
 
99
- def check_dependencies() -> Dict[str, bool]:
100
- """检查依赖"""
101
- deps = {
102
- "python": sys.version_info >= (3, 9),
103
- "torch": False,
186
+ def check_dependencies() -> Dict[str, bool]:
187
+ """检查依赖"""
188
+ deps = {
189
+ "python": sys.version_info >= (3, 9),
190
+ "torch": False,
104
191
  "transformers": False,
105
192
  "cuda": False
106
193
  }
@@ -118,37 +205,75 @@ def check_dependencies() -> Dict[str, bool]:
118
205
  except ImportError:
119
206
  pass
120
207
 
121
- return deps
122
-
123
-
124
- def install_dependencies(progress_callback=None):
125
- """安装依赖"""
126
- requirements = [
127
- "torch>=2.0.0",
128
- "transformers>=4.35.0",
208
+ return deps
209
+
210
+
211
+ def _pip_install(
212
+ requirement: str,
213
+ progress_callback=None,
214
+ index_url: Optional[str] = None,
215
+ extra_args: Optional[List[str]] = None
216
+ ):
217
+ if progress_callback:
218
+ progress_callback(f"Installing {requirement}...")
219
+
220
+ command = [sys.executable, "-m", "pip", "install", requirement]
221
+ if index_url:
222
+ command.extend(["--index-url", index_url])
223
+ if extra_args:
224
+ command.extend(extra_args)
225
+
226
+ result = subprocess.run(
227
+ command,
228
+ capture_output=True,
229
+ text=True
230
+ )
231
+
232
+ if result.returncode != 0:
233
+ raise Exception(f"Failed to install {requirement}: {result.stderr}")
234
+
235
+
236
+ def install_dependencies(progress_callback=None):
237
+ """安装依赖"""
238
+ requirements = [
239
+ "torch>=2.0.0",
240
+ "transformers>=4.35.0",
129
241
  "diffusers>=0.24.0",
130
242
  "accelerate>=0.24.0",
131
243
  "peft>=0.6.0",
132
244
  "bitsandbytes>=0.41.0",
133
245
  "httpx>=0.25.0",
134
246
  "pyyaml>=6.0",
135
- "pydantic>=2.0.0",
136
- "fastapi>=0.100.0",
137
- "uvicorn>=0.23.0"
138
- ]
139
-
140
- for req in requirements:
141
- if progress_callback:
142
- progress_callback(f"Installing {req}...")
143
-
144
- result = subprocess.run(
145
- [sys.executable, "-m", "pip", "install", req],
146
- capture_output=True,
147
- text=True
148
- )
149
-
150
- if result.returncode != 0:
151
- raise Exception(f"Failed to install {req}: {result.stderr}")
247
+ "pydantic>=2.0.0",
248
+ "fastapi>=0.100.0",
249
+ "uvicorn>=0.23.0"
250
+ ]
251
+
252
+ cuda_version = _detect_cuda_version()
253
+ torch_index_url = _select_torch_index_url(cuda_version)
254
+
255
+ if torch_index_url:
256
+ _pip_install(
257
+ "torch",
258
+ progress_callback,
259
+ index_url=torch_index_url,
260
+ extra_args=["--upgrade", "--force-reinstall"]
261
+ )
262
+ else:
263
+ if cuda_version and progress_callback:
264
+ progress_callback(
265
+ f"检测到 CUDA {cuda_version['major']}.{cuda_version['minor']},"
266
+ "无匹配的 PyTorch 版本,将安装 CPU 版"
267
+ )
268
+ _pip_install("torch>=2.0.0", progress_callback)
269
+
270
+ non_torch_requirements = [
271
+ item for item in requirements
272
+ if not re.match(r"^torch([<>=!~].*)?$", item.strip())
273
+ ]
274
+
275
+ for req in non_torch_requirements:
276
+ _pip_install(req, progress_callback)
152
277
 
153
278
 
154
279
  def save_config(config: Dict[str, Any], path: str = CONFIG_FILE):
@@ -317,10 +442,25 @@ class ConfigWizard:
317
442
  'enable_cpu_offload': gpu_info['memory_gb'] < 16
318
443
  }
319
444
  else:
320
- if RICH_AVAILABLE:
321
- console.print("[yellow]未检测到 GPU,将使用 CPU 模式(性能有限)[/yellow]")
445
+ nvidia_memory_gb = gpu_info.get("nvidia_memory_gb")
446
+ if gpu_info.get("nvidia_detected"):
447
+ if RICH_AVAILABLE:
448
+ console.print("[yellow]检测到 NVIDIA GPU,但 CUDA 版 PyTorch 不可用,将使用 CPU 模式(性能有限)[/yellow]")
449
+ console.print(f" 型号: {gpu_info.get('nvidia_model', 'Unknown')}")
450
+ if nvidia_memory_gb:
451
+ console.print(f" 显存: {nvidia_memory_gb} GB")
452
+ console.print("[yellow]请安装 CUDA 版 PyTorch(例如:python -m pip install torch --index-url https://download.pytorch.org/whl/cu121)[/yellow]")
453
+ else:
454
+ print("检测到 NVIDIA GPU,但 CUDA 版 PyTorch 不可用,将使用 CPU 模式(性能有限)")
455
+ print(f" 型号: {gpu_info.get('nvidia_model', 'Unknown')}")
456
+ if nvidia_memory_gb:
457
+ print(f" 显存: {nvidia_memory_gb} GB")
458
+ print("请安装 CUDA 版 PyTorch(例如:python -m pip install torch --index-url https://download.pytorch.org/whl/cu121)")
322
459
  else:
323
- print("未检测到 GPU,将使用 CPU 模式(性能有限)")
460
+ if RICH_AVAILABLE:
461
+ console.print("[yellow]未检测到 GPU,将使用 CPU 模式(性能有限)[/yellow]")
462
+ else:
463
+ print("未检测到 GPU,将使用 CPU 模式(性能有限)")
324
464
 
325
465
  self.config['gpu'] = {
326
466
  'model': 'CPU',
@@ -628,9 +768,17 @@ def cmd_status(args):
628
768
 
629
769
  # GPU信息
630
770
  gpu_info = check_gpu()
631
- print(f"\nGPU: {gpu_info['model'] if gpu_info['available'] else '未检测到'}")
632
771
  if gpu_info['available']:
772
+ print(f"\nGPU: {gpu_info['model']}")
633
773
  print(f"显存: {gpu_info['memory_gb']} GB")
774
+ elif gpu_info.get("nvidia_detected"):
775
+ nvidia_memory_gb = gpu_info.get("nvidia_memory_gb")
776
+ print(f"\nGPU: {gpu_info.get('nvidia_model', 'Unknown')} (驱动可用,PyTorch CUDA 不可用)")
777
+ if nvidia_memory_gb:
778
+ print(f"显存: {nvidia_memory_gb} GB")
779
+ print("建议安装 CUDA 版 PyTorch(例如:python -m pip install torch --index-url https://download.pytorch.org/whl/cu121)")
780
+ else:
781
+ print("\nGPU: 未检测到")
634
782
 
635
783
  # 配置信息
636
784
  print(f"\n配置:")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gpu-worker",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Distributed GPU Inference Worker - Share idle GPU computing power for LLM and image generation",
5
5
  "keywords": [
6
6
  "gpu",
@@ -27,7 +27,7 @@
27
27
  "url": "https://github.com/Baozhi888/distributed-gpu-inference/issues"
28
28
  },
29
29
  "bin": {
30
- "gpu-worker": "./bin/gpu-worker.js"
30
+ "gpu-worker": "bin/gpu-worker.js"
31
31
  },
32
32
  "scripts": {
33
33
  "postinstall": "node scripts/postinstall.js",