diff --git a/backend/python/app/api/chat_skill.py b/backend/python/app/api/chat_skill.py index 453f62c..a69f3cd 100644 --- a/backend/python/app/api/chat_skill.py +++ b/backend/python/app/api/chat_skill.py @@ -1740,3 +1740,112 @@ def _execute_install_app(db: Session, url: str, user_id, app_type: str = "auto") db.commit() return f"已成功安装「{_name}」({detected})。你现在可以直接在对话中使用它,或在应用市场查看。" + + + + +# Phase 1: Enhanced SKILL.md parser with 9-block standard +SKILLMD_SCHEMA = { + "name": "name", + "version": "version", + "author": "author", + "description": "description", + "capabilities": "capabilities", + "inputs": "inputs", + "outputs": "outputs", + "permissions": "permissions", + "dependencies": "dependencies", + "pricing": "pricing", +} + +def _parse_skillmd_v2(content: str) -> dict: + """Phase 1: Parse SKILL.md with standardized 9-block schema. + + SKILL.md Standard v2.0 format: + --- + name: Skill Name + version: 1.0.0 + author: Author Name + description: | + Multi-line description + capabilities: [text_generation, code_execution] + inputs: + - name: input_text + type: string + description: The input text + outputs: + - name: result + type: string + description: The processed result + permissions: [file_read, network] + dependencies: [python>=3.10, requests] + pricing: + free_tier: 10/day + pro_tier: 100/day + --- + Skill instruction content... + """ + import re, yaml + + result = { + "name": None, "version": "1.0.0", "author": None, + "description": None, "capabilities": [], "inputs": [], + "outputs": [], "permissions": [], "dependencies": [], + "pricing": {}, "instructions": "", "valid": False, "errors": [] + } + + if not content or not content.strip(): + result["errors"].append("Empty content") + return result + + # Extract YAML frontmatter + fm_match = re.match(r'^---\s*\n(.*?)\n---\s*\n?(.*)', content, re.DOTALL) + + if fm_match: + try: + fm = yaml.safe_load(fm_match.group(1)) + if isinstance(fm, dict): + for key in SKILLMD_SCHEMA: + if key in fm: + result[key] = fm[key] + result["valid"] = True + except yaml.YAMLError as e: + result["errors"].append(f"YAML parse error: {e}") + result["instructions"] = fm_match.group(2).strip() + else: + # No frontmatter: treat entire content as instructions + result["instructions"] = content.strip() + result["description"] = content[:200].strip() + + # Validate required fields + if not result["name"]: + result["errors"].append("Missing required field: name") + if not result["description"]: + result["errors"].append("Missing recommended field: description") + + result["valid"] = len([e for e in result["errors"] if "Missing required" in e]) == 0 + + return result + + +def _validate_skillmd(content: str) -> dict: + """Validate a SKILL.md content against the v2 schema. Returns {valid, errors, warnings}.""" + parsed = _parse_skillmd_v2(content) + errors = list(parsed["errors"]) + warnings = [] + + if not parsed["name"]: + errors.append("REQUIRED: 'name' field is missing") + if not parsed["description"]: + warnings.append("Recommended: add 'description' field") + if not parsed["version"]: + warnings.append("Recommended: specify 'version' (e.g. 1.0.0)") + if not parsed["capabilities"]: + warnings.append("Recommended: list 'capabilities' for marketplace visibility") + + return { + "valid": len(errors) == 0, + "errors": errors, + "warnings": warnings, + "parsed": parsed + } diff --git a/docker-compose.yml b/docker-compose.yml index 8b19be2..b06a493 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,7 +48,7 @@ services: - DEFAULT_MODEL=deepseek-ai/DeepSeek-V4-Flash - HTTP_PROXY=http://172.18.0.1:7890 - HTTPS_PROXY=http://172.18.0.1:7890 - - NO_PROXY=localhost,127.0.0.1,::1,210.16.161.218,172.17.0.0/16,172.18.0.0/16,172.19.0.0/16,172.20.0.0/16,10.0.0.0/8,192.168.0.0/16,.svc,.local,casdoor,qihu-aiflow-mysql,qihu-aiflow-redis,qihu-aiflow-pgvector,qihu-aiflow-pg18,qihu-aiflow-casdoor,qihuyun.com,ai.qihuyun.com,docs.qihuyun.com,bot.qihuyun.com,siliconflow.cn,api.siliconflow.cn,deepseek.com,api.deepseek.com,volcengineapi.com,aliyun.com,aliyuncs.com,dysmsapi.aliyuncs.com,tencentcloudapi.com,baidubce.com,dashscope.com,qwen.ai,zhipuai.cn,moonshot.cn,minimax.chat,baichuan.com,chatglm.cn,xiaomimimo.com,token-plan-cn.xiaomimimo.com,mi.com,pypi.tuna.tsinghua.edu.cn,feishu.cn,open.feishu.cn,dingtalk.com,open.dingtalk.com,weixin.qq.com,developers.weixin.qq.com,qyapi.weixin.qq.com,ilinkai.weixin.qq.com,tencent.com + - NO_PROXY=localhost,127.0.0.1,::1,210.16.161.2,qihu-aiflow-litellm,qihu-aiflow-meilisearch,qihu-aiflow-pg18,qihu-aiflow-redis,qihu-aiflow-casdoor18,172.17.0.0/16,172.18.0.0/16,172.19.0.0/16,172.20.0.0/16,10.0.0.0/8,192.168.0.0/16,.svc,.local,casdoor,qihu-aiflow-mysql,qihu-aiflow-redis,qihu-aiflow-pgvector,qihu-aiflow-pg18,qihu-aiflow-casdoor,qihuyun.com,ai.qihuyun.com,docs.qihuyun.com,bot.qihuyun.com,siliconflow.cn,api.siliconflow.cn,deepseek.com,api.deepseek.com,volcengineapi.com,aliyun.com,aliyuncs.com,dysmsapi.aliyuncs.com,tencentcloudapi.com,baidubce.com,dashscope.com,qwen.ai,zhipuai.cn,moonshot.cn,minimax.chat,baichuan.com,chatglm.cn,xiaomimimo.com,token-plan-cn.xiaomimimo.com,mi.com,pypi.tuna.tsinghua.edu.cn,feishu.cn,open.feishu.cn,dingtalk.com,open.dingtalk.com,weixin.qq.com,developers.weixin.qq.com,qyapi.weixin.qq.com,ilinkai.weixin.qq.com,tencent.com depends_on: redis: condition: service_started @@ -98,7 +98,7 @@ services: - AGNES_API_KEY=sk-idsplzeccvmbtyqxcrbhaiwmkzwajmicliunkjkouivepaaz - LITELLM_CONFIG=/app/config.yaml - MIMO_API_KEY=sk-idsplzeccvmbtyqxcrbhaiwmkzwajmicliunkjkouivepaaz - - DATABASE_URL=postgresql://aiflow:pwd2026@qihu-aiflow-pg18:5432/aiflow + - DATABASE_URL=postgresql://aiflow:pwd2026@qihu-aiflow-pg18:5432/litellm - LITELLM_MODE=PRODUCTION restart: unless-stopped networks: