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.
- package/core/deps_checker.py +59 -15
- package/core/llm.py +65 -30
- package/core/update_manager.py +1 -1
- package/core/version.py +9 -4
- package/install/install.ps1 +97 -49
- package/install/install.sh +55 -38
- package/install/uninstall.ps1 +142 -0
- package/install/uninstall.sh +149 -0
- package/main.py +18 -1
- package/memory/manager.py +60 -0
- package/package.json +1 -1
- package/setup.py +1 -1
- package/start.js +113 -8
- package/web/api_server.py +174 -75
- package/web/ui/chat.html +91 -11
- package/web/ui/index.html +58 -17
package/core/deps_checker.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 =
|
|
175
|
+
last_error = stderr.strip()
|
|
154
176
|
# 如果已经安装(满足要求),也算成功
|
|
155
|
-
if "Requirement already satisfied" in
|
|
156
|
-
"Requirement already satisfied" in
|
|
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 = "安装超时(
|
|
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
|
|
229
|
+
if "Chromium" in stdout and "already" in stdout.lower():
|
|
186
230
|
return True, "Chromium 浏览器已就绪"
|
|
187
|
-
return False, f"Chromium 安装失败: {
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
284
|
-
|
|
320
|
+
base_url = self.base_url or "https://open.bigmodel.cn/api/paas/v4/"
|
|
321
|
+
self.base_url = base_url
|
|
285
322
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
|
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
|
|
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":
|
package/core/update_manager.py
CHANGED
|
@@ -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.
|
|
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'
|
|
28
|
-
|
|
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 ""
|
package/install/install.ps1
CHANGED
|
@@ -9,7 +9,7 @@ param(
|
|
|
9
9
|
|
|
10
10
|
$ErrorActionPreference = "Stop"
|
|
11
11
|
$PKG_NAME = "myagent-ai"
|
|
12
|
-
$PKG_VERSION = "1.5.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if ($
|
|
107
|
-
$
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
$
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Start-
|
|
137
|
-
|
|
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.
|
|
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
|
-
|
|
246
|
-
|
|
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
|
}
|