mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-20 05:27:59 +08:00
feat: key management and scheduled task tools
This commit is contained in:
@@ -70,33 +70,27 @@ def should_include_skill(
|
||||
entry: SkillEntry,
|
||||
config: Optional[Dict] = None,
|
||||
current_platform: Optional[str] = None,
|
||||
lenient: bool = True,
|
||||
) -> bool:
|
||||
"""
|
||||
Determine if a skill should be included based on requirements.
|
||||
|
||||
Similar to clawdbot's shouldIncludeSkill logic, but with lenient mode:
|
||||
- In lenient mode (default): Only check explicit disable and platform, ignore missing requirements
|
||||
- In strict mode: Check all requirements (binary, env vars, config)
|
||||
Simple rule: Skills are auto-enabled if their requirements are met.
|
||||
- Has required API keys → enabled
|
||||
- Missing API keys → disabled
|
||||
- Wrong keys → enabled but will fail at runtime (LLM will handle error)
|
||||
|
||||
:param entry: SkillEntry to check
|
||||
:param config: Configuration dictionary
|
||||
:param config: Configuration dictionary (currently unused, reserved for future)
|
||||
:param current_platform: Current platform (default: auto-detect)
|
||||
:param lenient: If True, ignore missing requirements and load all skills (default: True)
|
||||
:return: True if skill should be included
|
||||
"""
|
||||
metadata = entry.metadata
|
||||
skill_name = entry.skill.name
|
||||
skill_config = get_skill_config(config, skill_name)
|
||||
|
||||
# Always check if skill is explicitly disabled in config
|
||||
if skill_config and skill_config.get('enabled') is False:
|
||||
return False
|
||||
|
||||
# No metadata = always include (no requirements)
|
||||
if not metadata:
|
||||
return True
|
||||
|
||||
# Always check platform requirements (can't work on wrong platform)
|
||||
# Check platform requirements (can't work on wrong platform)
|
||||
if metadata.os:
|
||||
platform_name = current_platform or resolve_runtime_platform()
|
||||
# Map common platform names
|
||||
@@ -114,12 +108,7 @@ def should_include_skill(
|
||||
if metadata.always:
|
||||
return True
|
||||
|
||||
# In lenient mode, skip requirement checks and load all skills
|
||||
# Skills will fail gracefully at runtime if requirements are missing
|
||||
if lenient:
|
||||
return True
|
||||
|
||||
# Strict mode: Check all requirements
|
||||
# Check requirements
|
||||
if metadata.requires:
|
||||
# Check required binaries (all must be present)
|
||||
required_bins = metadata.requires.get('bins', [])
|
||||
@@ -133,29 +122,13 @@ def should_include_skill(
|
||||
if not has_any_binary(any_bins):
|
||||
return False
|
||||
|
||||
# Check environment variables (with config fallback)
|
||||
# Check environment variables (API keys)
|
||||
# Simple rule: All required env vars must be set
|
||||
required_env = metadata.requires.get('env', [])
|
||||
if required_env:
|
||||
for env_name in required_env:
|
||||
# Check in order: 1) env var, 2) skill config env, 3) skill config apiKey (if primaryEnv)
|
||||
if has_env_var(env_name):
|
||||
continue
|
||||
if skill_config:
|
||||
# Check skill config env dict
|
||||
skill_env = skill_config.get('env', {})
|
||||
if isinstance(skill_env, dict) and env_name in skill_env:
|
||||
continue
|
||||
# Check skill config apiKey (if this is the primaryEnv)
|
||||
if metadata.primary_env == env_name and skill_config.get('apiKey'):
|
||||
continue
|
||||
# Requirement not satisfied
|
||||
return False
|
||||
|
||||
# Check config paths
|
||||
required_config = metadata.requires.get('config', [])
|
||||
if required_config and config:
|
||||
for config_path in required_config:
|
||||
if not is_config_path_truthy(config, config_path):
|
||||
if not has_env_var(env_name):
|
||||
# Missing required API key → disable skill
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -34,6 +34,7 @@ def format_skills_for_prompt(skills: List[Skill]) -> str:
|
||||
lines.append(f" <name>{_escape_xml(skill.name)}</name>")
|
||||
lines.append(f" <description>{_escape_xml(skill.description)}</description>")
|
||||
lines.append(f" <location>{_escape_xml(skill.file_path)}</location>")
|
||||
lines.append(f" <base_dir>{_escape_xml(skill.base_dir)}</base_dir>")
|
||||
lines.append(" </skill>")
|
||||
|
||||
lines.append("</available_skills>")
|
||||
|
||||
@@ -23,7 +23,22 @@ def parse_frontmatter(content: str) -> Dict[str, Any]:
|
||||
|
||||
frontmatter_text = match.group(1)
|
||||
|
||||
# Simple YAML-like parsing (supports key: value format)
|
||||
# Try to use PyYAML for proper YAML parsing
|
||||
try:
|
||||
import yaml
|
||||
frontmatter = yaml.safe_load(frontmatter_text)
|
||||
if not isinstance(frontmatter, dict):
|
||||
frontmatter = {}
|
||||
return frontmatter
|
||||
except ImportError:
|
||||
# Fallback to simple parsing if PyYAML not available
|
||||
pass
|
||||
except Exception:
|
||||
# If YAML parsing fails, fall back to simple parsing
|
||||
pass
|
||||
|
||||
# Simple YAML-like parsing (supports key: value format only)
|
||||
# This is a fallback for when PyYAML is not available
|
||||
for line in frontmatter_text.split('\n'):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
@@ -72,10 +87,8 @@ def parse_metadata(frontmatter: Dict[str, Any]) -> Optional[SkillMetadata]:
|
||||
if not isinstance(metadata_raw, dict):
|
||||
return None
|
||||
|
||||
# Support both 'moltbot' and 'cow' keys for compatibility
|
||||
meta_obj = metadata_raw.get('moltbot') or metadata_raw.get('cow')
|
||||
if not meta_obj or not isinstance(meta_obj, dict):
|
||||
return None
|
||||
# Use metadata_raw directly (COW format)
|
||||
meta_obj = metadata_raw
|
||||
|
||||
# Parse install specs
|
||||
install_specs = []
|
||||
|
||||
@@ -82,32 +82,24 @@ class SkillManager:
|
||||
self,
|
||||
skill_filter: Optional[List[str]] = None,
|
||||
include_disabled: bool = False,
|
||||
check_requirements: bool = False, # Changed default to False for lenient loading
|
||||
lenient: bool = True, # New parameter for lenient mode
|
||||
) -> List[SkillEntry]:
|
||||
"""
|
||||
Filter skills based on criteria.
|
||||
|
||||
By default (lenient=True), all skills are loaded regardless of missing requirements.
|
||||
Skills will fail gracefully at runtime if requirements are not met.
|
||||
Simple rule: Skills are auto-enabled if requirements are met.
|
||||
- Has required API keys → included
|
||||
- Missing API keys → excluded
|
||||
|
||||
:param skill_filter: List of skill names to include (None = all)
|
||||
:param include_disabled: Whether to include skills with disable_model_invocation=True
|
||||
:param check_requirements: Whether to check skill requirements (default: False)
|
||||
:param lenient: If True, ignore missing requirements (default: True)
|
||||
:return: Filtered list of skill entries
|
||||
"""
|
||||
from agent.skills.config import should_include_skill
|
||||
|
||||
entries = list(self.skills.values())
|
||||
|
||||
# Check requirements (platform, explicit disable, etc.)
|
||||
# In lenient mode, only checks platform and explicit disable
|
||||
if check_requirements or not lenient:
|
||||
entries = [e for e in entries if should_include_skill(e, self.config, lenient=lenient)]
|
||||
else:
|
||||
# Lenient mode: only check explicit disable and platform
|
||||
entries = [e for e in entries if should_include_skill(e, self.config, lenient=True)]
|
||||
# Check requirements (platform, binaries, env vars)
|
||||
entries = [e for e in entries if should_include_skill(e, self.config)]
|
||||
|
||||
# Apply skill filter
|
||||
if skill_filter is not None:
|
||||
|
||||
Reference in New Issue
Block a user