myagent-ai 1.5.2 → 1.5.4

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.
@@ -22,6 +22,7 @@ core/deps_checker.py - 自动依赖检测与安装
22
22
  from __future__ import annotations
23
23
 
24
24
  import importlib.util
25
+ import os
25
26
  import subprocess
26
27
  import sys
27
28
  import threading
@@ -116,6 +117,24 @@ def _get_python_executable() -> str:
116
117
  return sys.executable
117
118
 
118
119
 
120
+ def _get_pip_index_args() -> List[str]:
121
+ """获取 pip 镜像源参数。国内环境自动使用清华镜像,避免超时。"""
122
+ # 检测是否需要使用国内镜像(通过环境变量或语言环境判断)
123
+ use_mirror = os.environ.get("MYAGENT_PIP_MIRROR", "").lower() in ("1", "true", "yes", "cn")
124
+ if not use_mirror:
125
+ # 通过语言环境判断是否在国内
126
+ lang = os.environ.get("LANG", "").lower()
127
+ lc_all = os.environ.get("LC_ALL", "").lower()
128
+ if "zh_cn" in lang or "zh_cn" in lc_all or "chinese" in lang:
129
+ use_mirror = True
130
+
131
+ if use_mirror:
132
+ # 依次尝试的国内镜像(主源 + 备用)
133
+ return ["-i", "https://pypi.tuna.tsinghua.edu.cn/simple",
134
+ "--trusted-host", "pypi.tuna.tsinghua.edu.cn"]
135
+ return []
136
+
137
+
119
138
  def _pip_install(pip_names: List[str], category: str = "") -> Tuple[bool, str]:
120
139
  """
121
140
  使用当前 Python 解释器执行 pip install。
@@ -126,17 +145,17 @@ def _pip_install(pip_names: List[str], category: str = "") -> Tuple[bool, str]:
126
145
  python = _get_python_executable()
127
146
  packages = " ".join(pip_names)
128
147
 
129
- # 构建安装命令
130
- cmd = [python, "-m", "pip", "install", "--quiet"] + pip_names
148
+ # 镜像源参数(国内用户自动使用清华镜像)
149
+ mirror_args = _get_pip_index_args()
131
150
 
132
151
  # 处理 PEP668 (externally-managed-environment)
133
152
  # 尝试: 1) 直接安装 2) --break-system-packages 3) 用户空间 --user
134
153
  install_attempts = [
135
- [python, "-m", "pip", "install", "--quiet", "--disable-pip-version-check"] + pip_names,
154
+ [python, "-m", "pip", "install", "--quiet", "--disable-pip-version-check"] + mirror_args + pip_names,
136
155
  [python, "-m", "pip", "install", "--quiet", "--disable-pip-version-check",
137
- "--break-system-packages"] + pip_names,
156
+ "--break-system-packages"] + mirror_args + pip_names,
138
157
  [python, "-m", "pip", "install", "--quiet", "--disable-pip-version-check",
139
- "--user"] + pip_names,
158
+ "--user"] + mirror_args + pip_names,
140
159
  ]
141
160
 
142
161
  last_error = ""
@@ -145,23 +164,47 @@ def _pip_install(pip_names: List[str], category: str = "") -> Tuple[bool, str]:
145
164
  result = subprocess.run(
146
165
  attempt_cmd,
147
166
  capture_output=True,
148
- text=True,
149
- timeout=120, # 2分钟超时
167
+ timeout=180, # 3分钟超时(国内镜像可能较慢)
150
168
  )
169
+ # 使用 utf-8 + errors="replace" 解码输出,避免 Windows GBK 编码错误
170
+ stdout = result.stdout.decode("utf-8", errors="replace") if result.stdout else ""
171
+ stderr = result.stderr.decode("utf-8", errors="replace") if result.stderr else ""
172
+
151
173
  if result.returncode == 0:
152
174
  return True, f"已安装: {packages}"
153
- last_error = result.stderr.strip()
175
+ last_error = stderr.strip()
154
176
  # 如果已经安装(满足要求),也算成功
155
- if "Requirement already satisfied" in result.stdout or \
156
- "Requirement already satisfied" in result.stderr:
177
+ if "Requirement already satisfied" in stdout or \
178
+ "Requirement already satisfied" in stderr:
157
179
  return True, f"已就绪: {packages}"
158
180
  except subprocess.TimeoutExpired:
159
- last_error = "安装超时(120秒)"
181
+ last_error = "安装超时(180秒)"
160
182
  continue
161
183
  except Exception as e:
162
184
  last_error = str(e)
163
185
  continue
164
186
 
187
+ # 所有尝试失败后,尝试使用阿里云镜像重试一次
188
+ if not mirror_args:
189
+ logger.info(f"默认源安装失败,尝试阿里云镜像: {packages}")
190
+ retry_cmd = [python, "-m", "pip", "install", "--quiet", "--disable-pip-version-check",
191
+ "-i", "https://mirrors.aliyun.com/pypi/simple/",
192
+ "--trusted-host", "mirrors.aliyun.com"] + pip_names
193
+ try:
194
+ result = subprocess.run(
195
+ retry_cmd,
196
+ capture_output=True,
197
+ timeout=180,
198
+ )
199
+ stdout = result.stdout.decode("utf-8", errors="replace") if result.stdout else ""
200
+ stderr = result.stderr.decode("utf-8", errors="replace") if result.stderr else ""
201
+ if result.returncode == 0 or "Requirement already satisfied" in stdout or \
202
+ "Requirement already satisfied" in stderr:
203
+ return True, f"已安装(阿里云镜像): {packages}"
204
+ last_error = stderr.strip()
205
+ except Exception as e:
206
+ last_error = str(e)
207
+
165
208
  return False, f"安装失败: {packages} - {last_error}"
166
209
 
167
210
 
@@ -176,15 +219,16 @@ def _install_playwright_browsers() -> Tuple[bool, str]:
176
219
  result = subprocess.run(
177
220
  [python, "-m", "playwright", "install", "chromium"],
178
221
  capture_output=True,
179
- text=True,
180
222
  timeout=300, # 5分钟超时(浏览器较大)
181
223
  )
224
+ stdout = result.stdout.decode("utf-8", errors="replace") if result.stdout else ""
225
+ stderr = result.stderr.decode("utf-8", errors="replace") if result.stderr else ""
182
226
  if result.returncode == 0:
183
227
  return True, "Chromium 浏览器已安装"
184
228
  # 检查是否已经安装
185
- if "Chromium" in result.stdout and "already" in result.stdout.lower():
229
+ if "Chromium" in stdout and "already" in stdout.lower():
186
230
  return True, "Chromium 浏览器已就绪"
187
- return False, f"Chromium 安装失败: {result.stderr[:200]}"
231
+ return False, f"Chromium 安装失败: {stderr[:200]}"
188
232
  except subprocess.TimeoutExpired:
189
233
  return False, "Chromium 安装超时(5分钟),请手动运行: playwright install chromium"
190
234
  except Exception as e:
@@ -339,7 +383,7 @@ def check_and_install_deps(
339
383
  try:
340
384
  result = subprocess.run(
341
385
  [_get_python_executable(), "-m", "playwright", "install", "--dry-run", "chromium"],
342
- capture_output=True, text=True, timeout=10,
386
+ capture_output=True, timeout=10,
343
387
  )
344
388
  if result.returncode == 0:
345
389
  stats["playwright_browser"] = "ready"
package/core/llm.py CHANGED
@@ -209,12 +209,15 @@ class LLMClient:
209
209
  # 客户端初始化
210
210
  # ------------------------------------------------------------------
211
211
 
212
+ # 所有使用 OpenAI 兼容接口的提供商
213
+ _OPENAI_COMPATIBLE_PROVIDERS = ("openai", "custom", "modelscope", "deepseek", "moonshot", "qwen", "dashscope")
214
+
212
215
  def _ensure_client(self):
213
216
  """延迟初始化 LLM 客户端"""
214
217
  if self._client is not None:
215
218
  return
216
219
 
217
- if self.provider in ("openai", "custom"):
220
+ if self.provider in self._OPENAI_COMPATIBLE_PROVIDERS:
218
221
  self._init_openai()
219
222
  elif self.provider == "anthropic":
220
223
  self._init_anthropic()
@@ -223,21 +226,38 @@ class LLMClient:
223
226
  elif self.provider == "zhipu":
224
227
  self._init_zhipu()
225
228
  else:
226
- raise ValueError(f"不支持的 LLM 提供商: {self.provider}")
229
+ # 未知提供商默认尝试 OpenAI 兼容接口(大多数 API 都是 OpenAI 兼容的)
230
+ logger.warning(f"未知 LLM 提供商 '{self.provider}',尝试 OpenAI 兼容接口")
231
+ self._init_openai()
227
232
 
228
233
  def _init_openai(self):
229
234
  """初始化 OpenAI / 兼容客户端"""
230
235
  try:
231
236
  from openai import OpenAI
232
- kwargs = {}
233
- if self.api_key:
234
- kwargs["api_key"] = self.api_key
235
- if self.base_url:
236
- kwargs["base_url"] = self.base_url
237
- self._client = OpenAI(**kwargs)
238
- logger.info(f"OpenAI 客户端已初始化 (model={self.model})")
239
237
  except ImportError:
240
- raise ImportError("请安装 openai: pip install openai")
238
+ # 自动尝试安装 openai
239
+ logger.warning("openai 未安装,正在自动安装...")
240
+ try:
241
+ from core.deps_checker import _pip_install
242
+ success, msg = _pip_install(["openai>=1.12.0"])
243
+ if success:
244
+ logger.info(f"openai 自动安装成功: {msg}")
245
+ else:
246
+ logger.error(f"openai 自动安装失败: {msg}")
247
+ raise ImportError(f"请安装 openai: pip install openai (自动安装失败: {msg})")
248
+ except Exception as e:
249
+ if isinstance(e, ImportError):
250
+ raise
251
+ raise ImportError(f"请安装 openai: pip install openai (安装异常: {e})")
252
+ # 安装成功后重新导入
253
+ from openai import OpenAI
254
+ kwargs = {}
255
+ if self.api_key:
256
+ kwargs["api_key"] = self.api_key
257
+ if self.base_url:
258
+ kwargs["base_url"] = self.base_url
259
+ self._client = OpenAI(**kwargs)
260
+ logger.info(f"OpenAI 客户端已初始化 (model={self.model})")
241
261
 
242
262
  def _init_anthropic(self):
243
263
  """初始化 Anthropic 客户端"""
@@ -272,26 +292,41 @@ class LLMClient:
272
292
  """
273
293
  try:
274
294
  from openai import OpenAI
275
- import os
276
-
277
- api_key = self.api_key or os.environ.get("ZHIPUAI_API_KEY", "")
278
- if not api_key:
279
- raise ValueError(
280
- "Zhipu API Key 未设置,请传入 api_key 或设置 ZHIPUAI_API_KEY 环境变量"
281
- )
295
+ except ImportError:
296
+ # 自动尝试安装 openai
297
+ logger.warning("openai 未安装,正在自动安装...")
298
+ try:
299
+ from core.deps_checker import _pip_install
300
+ success, msg = _pip_install(["openai>=1.12.0"])
301
+ if success:
302
+ logger.info(f"openai 自动安装成功: {msg}")
303
+ else:
304
+ logger.error(f"openai 自动安装失败: {msg}")
305
+ raise ImportError(f"请安装 openai: pip install openai (自动安装失败: {msg})")
306
+ except Exception as e:
307
+ if isinstance(e, ImportError):
308
+ raise
309
+ raise ImportError(f"请安装 openai: pip install openai (安装异常: {e})")
310
+ # 安装成功后重新导入
311
+ from openai import OpenAI
312
+ import os
313
+
314
+ api_key = self.api_key or os.environ.get("ZHIPUAI_API_KEY", "")
315
+ if not api_key:
316
+ raise ValueError(
317
+ "Zhipu API Key 未设置,请传入 api_key 或设置 ZHIPUAI_API_KEY 环境变量"
318
+ )
282
319
 
283
- base_url = self.base_url or "https://open.bigmodel.cn/api/paas/v4/"
284
- self.base_url = base_url
320
+ base_url = self.base_url or "https://open.bigmodel.cn/api/paas/v4/"
321
+ self.base_url = base_url
285
322
 
286
- self._client = OpenAI(
287
- api_key=api_key,
288
- base_url=base_url,
289
- )
290
- if not self.model or self.model == "gpt-4":
291
- self.model = "glm-4-flash"
292
- logger.info(f"Zhipu GLM 客户端已初始化 (model={self.model}, url={base_url})")
293
- except ImportError:
294
- raise ImportError("请安装 openai: pip install openai")
323
+ self._client = OpenAI(
324
+ api_key=api_key,
325
+ base_url=base_url,
326
+ )
327
+ if not self.model or self.model == "gpt-4":
328
+ self.model = "glm-4-flash"
329
+ logger.info(f"Zhipu GLM 客户端已初始化 (model={self.model}, url={base_url})")
295
330
 
296
331
  # ------------------------------------------------------------------
297
332
  # 核心 Chat 方法 (含重试)
@@ -375,7 +410,7 @@ class LLMClient:
375
410
  request_kwargs.update(kwargs)
376
411
 
377
412
  try:
378
- if self.provider in ("openai", "custom", "zhipu"):
413
+ if self.provider in self._OPENAI_COMPATIBLE_PROVIDERS or self.provider == "zhipu":
379
414
  response = await self._run_with_retry(self._chat_openai, request_kwargs)
380
415
  elif self.provider == "anthropic":
381
416
  response = await self._run_with_retry(
@@ -581,7 +616,7 @@ class LLMClient:
581
616
  request_kwargs.update(kwargs)
582
617
 
583
618
  try:
584
- if self.provider in ("openai", "custom", "zhipu"):
619
+ if self.provider in self._OPENAI_COMPATIBLE_PROVIDERS or self.provider == "zhipu":
585
620
  async for chunk in self._stream_openai(request_kwargs):
586
621
  yield chunk
587
622
  elif self.provider == "anthropic":
@@ -317,7 +317,7 @@ class UpdateManager:
317
317
  if not pkg_json.exists():
318
318
  return ""
319
319
 
320
- pkg_data = json.loads(pkg_json.read_text())
320
+ pkg_data = json.loads(pkg_json.read_text(encoding="utf-8"))
321
321
  pkg_name = pkg_data.get("name", "")
322
322
  if not pkg_name:
323
323
  return ""
package/core/version.py CHANGED
@@ -11,11 +11,11 @@ import subprocess
11
11
  from pathlib import Path
12
12
 
13
13
  # ── 基线版本(与 setup.py / package.json 保持一致) ──
14
- BASE_VERSION = "1.5.0"
14
+ BASE_VERSION = "1.5.3"
15
15
 
16
16
 
17
17
  def _version_from_git() -> str:
18
- """尝试从 git describe 获取版本号"""
18
+ """尝试从 git describe 获取版本号(必须是 x.y.z 格式)"""
19
19
  try:
20
20
  result = subprocess.run(
21
21
  ["git", "describe", "--tags", "--always", "--dirty"],
@@ -24,8 +24,13 @@ def _version_from_git() -> str:
24
24
  )
25
25
  tag = result.stdout.strip()
26
26
  if tag:
27
- # 去掉 'v' 前缀,保留 dirty 标记
28
- return tag.lstrip("v")
27
+ # 去掉 'v' 前缀
28
+ tag = tag.lstrip("v")
29
+ # 只接受 x.y.z 格式的版本号,拒绝 commit hash
30
+ if tag and tag[0].isdigit() and "." in tag:
31
+ # 去掉 dirty 标记和 build metadata
32
+ clean = tag.split("-dirty")[0].split("+")[0]
33
+ return clean
29
34
  except Exception:
30
35
  pass
31
36
  return ""
@@ -9,7 +9,7 @@ param(
9
9
 
10
10
  $ErrorActionPreference = "Stop"
11
11
  $PKG_NAME = "myagent-ai"
12
- $PKG_VERSION = "1.5.2"
12
+ $PKG_VERSION = "1.5.3"
13
13
 
14
14
  # Allow running scripts for the current process
15
15
  if ($PSVersionTable.PSVersion.Major -ge 5) {
@@ -43,12 +43,14 @@ function Check-Python {
43
43
  $pyVer = (python --version 2>$null)
44
44
  if ($pyVer) {
45
45
  $verNum = [version]($pyVer -replace 'Python\s+(\S+)', '$1')
46
- if ($verNum -ge [version]"3.14") {
46
+ if ($verNum -ge [version]"3.10") {
47
47
  $Script:PythonCmd = "python"
48
48
  Write-Host "[OK] Python $verNum found" -ForegroundColor Green
49
49
  return $true
50
50
  } else {
51
- Write-Host "[!] Python $verNum found, upgrading to 3.14 ..." -ForegroundColor Yellow
51
+ Write-Host "[!] Python $verNum found, upgrading to 3.12+ ..." -ForegroundColor Yellow
52
+ # 仍然记录当前可用的 Python(即使版本低,也总比没有好)
53
+ $Script:PythonCmd = "python"
52
54
  return $false
53
55
  }
54
56
  }
@@ -60,15 +62,18 @@ function Check-Python {
60
62
  "$env:LOCALAPPDATA\Programs\Python\Python314\python.exe",
61
63
  "$env:LOCALAPPDATA\Programs\Python\Python313\python.exe",
62
64
  "$env:LOCALAPPDATA\Programs\Python\Python312\python.exe",
65
+ "$env:LOCALAPPDATA\Programs\Python\Python311\python.exe",
63
66
  "C:\Python314\python.exe",
64
67
  "C:\Python313\python.exe",
68
+ "C:\Python312\python.exe",
65
69
  "C:\Program Files\Python314\python.exe",
66
- "C:\Program Files\Python313\python.exe"
70
+ "C:\Program Files\Python313\python.exe",
71
+ "C:\Program Files\Python312\python.exe"
67
72
  )
68
73
  foreach ($pyPath in $commonPaths) {
69
74
  if (Test-Path $pyPath) {
70
75
  $verNum = (& $pyPath --version 2>$null) -replace 'Python\s+(\S+)', '$1'
71
- if ($verNum -and [version]$verNum -ge [version]"3.14") {
76
+ if ($verNum -and [version]$verNum -ge [version]"3.10") {
72
77
  $Script:PythonCmd = $pyPath
73
78
  Write-Host "[OK] Python $verNum found at $pyPath" -ForegroundColor Green
74
79
  return $true
@@ -79,35 +84,38 @@ function Check-Python {
79
84
  }
80
85
 
81
86
  function Install-Python {
82
- Write-Host "[*] Installing Python 3.14 ..." -ForegroundColor Yellow
87
+ Write-Host "[*] Installing Python 3.12 ..." -ForegroundColor Yellow
83
88
  $installAttempted = $false
84
89
 
85
- # Try winget first
90
+ # Try winget first (try 3.14, then 3.13, then 3.12)
86
91
  try {
87
92
  $null = (winget --version 2>$null)
88
93
  Write-Host " Using winget ..." -ForegroundColor Gray
89
- winget install Python.Python.3.14 --accept-package-agreements --accept-source-agreements
90
- $installAttempted = $true
91
- # 等待安装完成后刷新 PATH(重试多次)
92
- for ($i = 1; $i -le 5; $i++) {
93
- Start-Sleep -Seconds 2
94
- if (Check-Python) { return }
95
- }
96
- # winget 已完成安装,即使 Check-Python 失败也不再重复安装
97
- Refresh-Path
98
- $pyPaths = @(
99
- "$env:LOCALAPPDATA\Programs\Python\Python314\python.exe",
100
- "C:\Python314\python.exe",
101
- "C:\Program Files\Python314\python.exe"
102
- )
103
- foreach ($pyPath in $pyPaths) {
104
- if (Test-Path $pyPath) {
105
- $verNum = (& $pyPath --version 2>$null) -replace 'Python\s+(\S+)', '$1'
106
- if ($verNum) {
107
- $Script:PythonCmd = $pyPath
108
- Write-Host "[OK] Python $verNum installed via winget at $pyPath" -ForegroundColor Green
109
- Write-Host "[i] PATH will be updated after restart. Using direct path for now." -ForegroundColor Gray
110
- return
94
+ foreach ($pyVer in @("3.14", "3.13", "3.12")) {
95
+ Write-Host " 尝试 Python $pyVer ..." -ForegroundColor Gray
96
+ winget install "Python.Python.$pyVer" --accept-package-agreements --accept-source-agreements 2>$null
97
+ $installAttempted = $true
98
+ # 等待安装完成后刷新 PATH(重试多次)
99
+ for ($i = 1; $i -le 5; $i++) {
100
+ Start-Sleep -Seconds 2
101
+ if (Check-Python) { return }
102
+ }
103
+ # winget 已完成安装,即使 Check-Python 失败也不再重复安装
104
+ Refresh-Path
105
+ $pyPaths = @(
106
+ "$env:LOCALAPPDATA\Programs\Python\Python$pyVer\python.exe",
107
+ "C:\Python$pyVer\python.exe",
108
+ "C:\Program Files\Python$pyVer\python.exe"
109
+ )
110
+ foreach ($pyPath in $pyPaths) {
111
+ if (Test-Path $pyPath) {
112
+ $verNum = (& $pyPath --version 2>$null) -replace 'Python\s+(\S+)', '$1'
113
+ if ($verNum) {
114
+ $Script:PythonCmd = $pyPath
115
+ Write-Host "[OK] Python $verNum installed via winget at $pyPath" -ForegroundColor Green
116
+ Write-Host "[i] PATH will be updated after restart. Using direct path for now." -ForegroundColor Gray
117
+ return
118
+ }
111
119
  }
112
120
  }
113
121
  }
@@ -120,29 +128,36 @@ function Install-Python {
120
128
 
121
129
  # 仅在 winget 未尝试时才从 python.org 下载(避免双重安装)
122
130
  if (-not $installAttempted) {
123
- # Fallback: download from python.org
124
- $pyUrl = "https://www.python.org/ftp/python/3.14.0/python-3.14.0-amd64.exe"
125
- $pyExe = Join-Path $env:TEMP "python-installer.exe"
126
-
127
- try {
128
- Write-Host " Downloading Python 3.14.0 ..." -ForegroundColor Gray
129
- Invoke-WebRequest -Uri $pyUrl -OutFile $pyExe -UseBasicParsing
130
-
131
- Write-Host " Installing (Add to PATH) ..." -ForegroundColor Gray
132
- Start-Process -FilePath $pyExe -ArgumentList "/quiet InstallAllUsers=1 PrependPath=1" -Wait
133
-
134
- # 刷新 PATH 并验证(重试多次)
135
- for ($i = 1; $i -le 5; $i++) {
136
- Start-Sleep -Seconds 2
137
- if (Check-Python) { return }
131
+ # Fallback: download from python.org (try 3.12 first - most stable)
132
+ $pyVersions = @(
133
+ @{ Url = "https://www.python.org/ftp/python/3.12.9/python-3.12.9-amd64.exe"; Ver = "3.12.9" },
134
+ @{ Url = "https://www.python.org/ftp/python/3.13.3/python-3.13.3-amd64.exe"; Ver = "3.13.3" },
135
+ @{ Url = "https://www.python.org/ftp/python/3.14.0/python-3.14.0-amd64.exe"; Ver = "3.14.0" }
136
+ )
137
+ foreach ($pyInfo in $pyVersions) {
138
+ $pyExe = Join-Path $env:TEMP "python-installer.exe"
139
+ try {
140
+ Write-Host " Downloading Python $($pyInfo.Ver) ..." -ForegroundColor Gray
141
+ Invoke-WebRequest -Uri $pyInfo.Url -OutFile $pyExe -UseBasicParsing -ErrorAction Stop
142
+
143
+ Write-Host " Installing (Add to PATH) ..." -ForegroundColor Gray
144
+ Start-Process -FilePath $pyExe -ArgumentList "/quiet InstallAllUsers=1 PrependPath=1" -Wait
145
+
146
+ # 刷新 PATH 并验证(重试多次)
147
+ for ($i = 1; $i -le 5; $i++) {
148
+ Start-Sleep -Seconds 2
149
+ if (Check-Python) { return }
150
+ }
151
+ } catch {
152
+ Write-Host " Python $($pyInfo.Ver) download failed, trying next..." -ForegroundColor Yellow
153
+ } finally {
154
+ if (Test-Path $pyExe) { Remove-Item -Force $pyExe -ErrorAction SilentlyContinue }
138
155
  }
139
- } finally {
140
- if (Test-Path $pyExe) { Remove-Item -Force $pyExe -ErrorAction SilentlyContinue }
141
156
  }
142
157
  }
143
158
 
144
159
  Write-Host "[x] Failed to install Python." -ForegroundColor Red
145
- Write-Host " Please install Python 3.14+ manually: https://www.python.org/downloads/" -ForegroundColor Gray
160
+ Write-Host " Please install Python 3.10+ manually: https://www.python.org/downloads/" -ForegroundColor Gray
146
161
  exit 1
147
162
  }
148
163
 
@@ -237,13 +252,46 @@ function Install-PythonDeps {
237
252
 
238
253
  Write-Host "[*] Creating virtual environment at $venvDir ..." -ForegroundColor Yellow
239
254
  New-Item -ItemType Directory -Path "$env:USERPROFILE\.myagent" -Force | Out-Null
255
+
256
+ # 如果旧 venv 存在且损坏,先删除重建
257
+ if ((Test-Path $venvPython)) {
258
+ try {
259
+ $null = (& $venvPython --version 2>$null)
260
+ } catch {
261
+ Write-Host "[!] 旧虚拟环境损坏,正在重建..." -ForegroundColor Yellow
262
+ Remove-Item -Recurse -Force $venvDir -ErrorAction SilentlyContinue
263
+ }
264
+ }
265
+
240
266
  & $pyCmd -m venv $venvDir
267
+ if (-not (Test-Path $venvPython)) {
268
+ Write-Host "[x] 虚拟环境创建失败!Python: $pyCmd" -ForegroundColor Red
269
+ Write-Host " 请手动安装: python -m venv $venvDir" -ForegroundColor Gray
270
+ return
271
+ }
241
272
  Write-Host "[OK] Virtual environment created" -ForegroundColor Green
242
273
 
243
274
  Write-Host "[*] Installing Python dependencies into venv ..." -ForegroundColor Yellow
244
275
  & $venvPython -m pip install --upgrade pip --quiet 2>$null
245
- & $venvPython -m pip install -r $reqFile --disable-pip-version-check
246
- Write-Host "[OK] Dependencies installed into venv" -ForegroundColor Green
276
+
277
+ # 尝试使用清华镜像安装(国内用户更快)
278
+ $mirrorArgs = @("-i", "https://pypi.tuna.tsinghua.edu.cn/simple", "--trusted-host", "pypi.tuna.tsinghua.edu.cn")
279
+ $pipResult = & $venvPython -m pip install -r $reqFile --disable-pip-version-check @mirrorArgs 2>&1
280
+ if ($LASTEXITCODE -ne 0) {
281
+ Write-Host "[!] 清华镜像安装失败,尝试默认源..." -ForegroundColor Yellow
282
+ $pipResult = & $venvPython -m pip install -r $reqFile --disable-pip-version-check 2>&1
283
+ }
284
+ if ($LASTEXITCODE -ne 0) {
285
+ Write-Host "[!] 默认源安装失败,尝试阿里云镜像..." -ForegroundColor Yellow
286
+ $aliyunArgs = @("-i", "https://mirrors.aliyun.com/pypi/simple/", "--trusted-host", "mirrors.aliyun.com")
287
+ $pipResult = & $venvPython -m pip install -r $reqFile --disable-pip-version-check @aliyunArgs 2>&1
288
+ }
289
+ if ($LASTEXITCODE -ne 0) {
290
+ Write-Host "[!] 部分依赖安装失败,但核心功能可在启动时自动安装" -ForegroundColor Yellow
291
+ Write-Host " 请运行 'myagent-ai reinstall' 重新安装" -ForegroundColor Gray
292
+ } else {
293
+ Write-Host "[OK] Dependencies installed into venv" -ForegroundColor Green
294
+ }
247
295
  Write-Host "[i] Virtual env: $venvDir" -ForegroundColor Gray
248
296
  Write-Host "[i] venv will be used automatically on startup" -ForegroundColor Gray
249
297
  }