Skip to content

Commit f4da5a6

Browse files
committed
fix bug
1 parent 6d0fe24 commit f4da5a6

File tree

2 files changed

+60
-15
lines changed

2 files changed

+60
-15
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ docker run -d \
239239
-e LANGFUSE_SECRET_KEY= \
240240
-e LANGFUSE_PUBLIC_KEY= \
241241
-e LANGFUSE_BASE_URL= \
242+
-e VITE_ENABLE_PAGE_AGENT=false \
243+
-e LLM_MAX_TOKENS=65536 \
242244
-p 18080:80 \
243245
-p 18088:8088 \
244246
-p 15432:5432 \
@@ -252,15 +254,15 @@ docker run -d \
252254
-v ./volume/logs/minio:/var/log/minio \
253255
-v ./volume/logs/postgresql:/var/log/postgresql \
254256
--add-host host.docker.internal:host-gateway \
255-
crpi-7xkxsdc0iki61l0q.cn-hangzhou.personal.cr.aliyuncs.com/apconw/aix-db:1.2.3
257+
crpi-7xkxsdc0iki61l0q.cn-hangzhou.personal.cr.aliyuncs.com/apconw/aix-db:1.2.4
256258
```
257259

258260
### 使用 Docker Compose
259261

260262
```bash
261263
git clone https://github.com/apconw/Aix-DB.git
262264
cd Aix-DB/docker
263-
cp .env.template .env # 复制环境变量模板,按需修改
265+
cp .env.template .env # 复制环境变量模板,按需修改(推荐开启 VITE_ENABLE_PAGE_AGENT=true)
264266
docker-compose up -d
265267
```
266268

services/skill_service.py

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import logging
6+
import re
67
import shutil
78
import subprocess
89
import sys
@@ -66,7 +67,27 @@ def _parse_skill_markdown(cls, file_path: Path) -> dict:
6667
if len(parts) >= 3:
6768
front_matter = parts[1].strip()
6869
# 用 yaml.safe_load 正确解析多行 description (">" 折叠语法)
69-
meta = yaml.safe_load(front_matter)
70+
try:
71+
meta = yaml.safe_load(front_matter)
72+
except yaml.YAMLError:
73+
# YAML 解析失败时(如 description 包含未转义的冒号),
74+
# 使用正则表达式提取 name 和 description
75+
meta = None
76+
name_match = re.search(
77+
r"^name:\s*(.+?)\s*$", front_matter, re.MULTILINE
78+
)
79+
desc_match = re.search(
80+
r"^description:\s*(.+?)\s*$", front_matter, re.MULTILINE
81+
)
82+
if name_match or desc_match:
83+
meta = {
84+
"name": (
85+
name_match.group(1).strip() if name_match else None
86+
),
87+
"description": (
88+
desc_match.group(1).strip() if desc_match else None
89+
),
90+
}
7091
if isinstance(meta, dict):
7192
return {
7293
"name": meta.get("name") or file_path.parent.name,
@@ -78,7 +99,9 @@ def _parse_skill_markdown(cls, file_path: Path) -> dict:
7899
return {"name": file_path.parent.name, "description": ""}
79100

80101
@classmethod
81-
async def install_from_github(cls, repo: str, skill_names: list[str] | None = None, scope: str = "common") -> list[dict]:
102+
async def install_from_github(
103+
cls, repo: str, skill_names: list[str] | None = None, scope: str = "common"
104+
) -> list[dict]:
82105
"""从 GitHub 仓库安装技能"""
83106
if scope not in ("common", "deep"):
84107
scope = "common"
@@ -89,7 +112,9 @@ async def install_from_github(cls, repo: str, skill_names: list[str] | None = No
89112

90113
for branch in branches:
91114
try:
92-
downloaded_skills, temp_dir = await cls._download_github_skills(repo, branch, skill_names)
115+
downloaded_skills, temp_dir = await cls._download_github_skills(
116+
repo, branch, skill_names
117+
)
93118
break
94119
except Exception as e:
95120
logger.warning(f"从 {repo} ({branch} 分支) 下载技能失败: {e}")
@@ -115,7 +140,9 @@ async def install_from_github(cls, repo: str, skill_names: list[str] | None = No
115140
return installed_skills
116141

117142
@classmethod
118-
async def _download_github_skills(cls, repo: str, branch: str, skill_names: list[str] | None = None) -> tuple[list[dict], Path]:
143+
async def _download_github_skills(
144+
cls, repo: str, branch: str, skill_names: list[str] | None = None
145+
) -> tuple[list[dict], Path]:
119146
"""从 GitHub 下载技能,返回 (skills列表, 临时目录)"""
120147
# 清理 repo 格式
121148
repo = repo.strip("/")
@@ -130,12 +157,16 @@ async def _download_github_skills(cls, repo: str, branch: str, skill_names: list
130157
async with httpx.AsyncClient(follow_redirects=True, timeout=60) as client:
131158
response = await client.get(zip_url)
132159
response.raise_for_status()
133-
skills, temp_dir = cls._extract_skills_from_zip(response.content, skill_names)
160+
skills, temp_dir = cls._extract_skills_from_zip(
161+
response.content, skill_names
162+
)
134163

135164
return skills, temp_dir
136165

137166
@classmethod
138-
def _extract_skills_from_zip(cls, zip_bytes: bytes, skill_names: list[str] | None = None) -> tuple[list[dict], Path]:
167+
def _extract_skills_from_zip(
168+
cls, zip_bytes: bytes, skill_names: list[str] | None = None
169+
) -> tuple[list[dict], Path]:
139170
"""从 zip 内容中提取技能信息,返回 (skills列表, 临时目录路径)"""
140171
temp_dir = Path(tempfile.mkdtemp())
141172

@@ -164,7 +195,7 @@ def _extract_skills_from_zip(cls, zip_bytes: bytes, skill_names: list[str] | Non
164195
stripped_name = actual_dir_name
165196
for suffix in ["-main", "-master"]:
166197
if stripped_name.endswith(suffix):
167-
stripped_name = stripped_name[:-len(suffix)]
198+
stripped_name = stripped_name[: -len(suffix)]
168199
if stripped_name != expected_dir_name:
169200
logger.warning(
170201
f"技能目录名 '{actual_dir_name}' (stripped: '{stripped_name}') "
@@ -199,7 +230,11 @@ def _setup_skill_venv(cls, skill_dir: Path) -> bool:
199230
)
200231

201232
# 确定 pip 的路径(跨平台兼容)
202-
pip_path = venv_dir / "bin" / "pip" if venv_dir.joinpath("bin").exists() else venv_dir / "Scripts" / "pip.exe"
233+
pip_path = (
234+
venv_dir / "bin" / "pip"
235+
if venv_dir.joinpath("bin").exists()
236+
else venv_dir / "Scripts" / "pip.exe"
237+
)
203238

204239
# 安装依赖
205240
subprocess.run(
@@ -247,7 +282,9 @@ def _install_skill(cls, skill_info: dict, scope: str = "common") -> bool:
247282
return True
248283

249284
@classmethod
250-
def install_from_zip(cls, zip_bytes: bytes, filename: str, scope: str = "common") -> list[dict]:
285+
def install_from_zip(
286+
cls, zip_bytes: bytes, filename: str, scope: str = "common"
287+
) -> list[dict]:
251288
"""从 zip 安装"""
252289
if scope not in ("common", "deep"):
253290
scope = "common"
@@ -273,7 +310,7 @@ def install_from_zip(cls, zip_bytes: bytes, filename: str, scope: str = "common"
273310
stripped_name = actual_dir_name
274311
for suffix in ["-main", "-master"]:
275312
if stripped_name.endswith(suffix):
276-
stripped_name = stripped_name[:-len(suffix)]
313+
stripped_name = stripped_name[: -len(suffix)]
277314

278315
# 检查是否只有一个 skill,且目录名不匹配(temp 目录情况)
279316
all_skill_mds = list(temp_dir.rglob("**/SKILL.md"))
@@ -282,7 +319,9 @@ def install_from_zip(cls, zip_bytes: bytes, filename: str, scope: str = "common"
282319

283320
if is_single_skill and name_mismatch:
284321
# 单 skill 的 zip,顶层是随机 temp 目录,直接使用
285-
logger.info(f"检测到单技能 zip(temp 目录模式),技能名: {skill_info['name']}")
322+
logger.info(
323+
f"检测到单技能 zip(temp 目录模式),技能名: {skill_info['name']}"
324+
)
286325
elif stripped_name != skill_info["name"]:
287326
logger.warning(
288327
f"技能目录名 '{actual_dir_name}' 与 SKILL.md 中的 name '{skill_info['name']}' 不一致,跳过"
@@ -330,7 +369,9 @@ def uninstall_skill(cls, skill_name: str, scope: str = "common") -> bool:
330369
return False
331370

332371
@classmethod
333-
def toggle_skill(cls, skill_name: str, enabled: bool, scope: str = "common") -> bool:
372+
def toggle_skill(
373+
cls, skill_name: str, enabled: bool, scope: str = "common"
374+
) -> bool:
334375
"""启用/禁用技能"""
335376
skills_dir = cls.get_skills_dir(scope)
336377
skill_dir = skills_dir / skill_name
@@ -356,7 +397,9 @@ def toggle_skill(cls, skill_name: str, enabled: bool, scope: str = "common") ->
356397
return False
357398

358399
@classmethod
359-
def get_enabled_skill_paths(cls, selected_skills: list[str] | None = None, scope: str = "common") -> list[str]:
400+
def get_enabled_skill_paths(
401+
cls, selected_skills: list[str] | None = None, scope: str = "common"
402+
) -> list[str]:
360403
"""获取生效的技能路径"""
361404
if selected_skills is not None:
362405
# 如果指定了技能列表,直接返回对应路径(忽略 .disabled)

0 commit comments

Comments
 (0)