ai-skills-api/examples/seed-data.py

310 lines
12 KiB
Python

#!/usr/bin/env python3
"""Seed the skills database with useful defaults"""
import httpx
BASE_URL = "http://helm:8675"
SKILLS = [
{
"id": "homelab-docker-compose",
"name": "Docker Compose Standard",
"category": "homelab",
"description": "Standard Docker Compose configuration patterns",
"content": """Always use docker-compose v3.8+. Include:
- health checks for all services
- restart: unless-stopped policy
- resource limits (memory, CPU)
- named volumes for persistent data
- .env file for secrets (never hardcode)
- explicit network definitions
- logging driver with size limits""",
"tags": ["docker", "compose", "infrastructure"]
},
{
"id": "homelab-traefik",
"name": "Traefik Reverse Proxy",
"category": "homelab",
"description": "Traefik configuration standards",
"content": """For Traefik reverse proxy setups:
- Use Docker provider with watched containers
- Enable ACME/Let's Encrypt for HTTPS
- Store certs in persistent volume
- Use middlewares for auth, rate limiting, redirects
- Label-based routing on containers
- Dashboard protected by auth middleware""",
"tags": ["traefik", "proxy", "https", "infrastructure"]
},
{
"id": "typescript-react",
"name": "React TypeScript Component",
"category": "coding",
"description": "Standard React component patterns with TypeScript",
"content": """React component standards:
- Use functional components with TypeScript interfaces
- Props defined as interface, not type alias
- Use React.FC only when children needed
- Prefer composition over inheritance
- Custom hooks for reusable logic
- Strict null checks enabled
- Avoid any, use unknown if needed""",
"tags": ["react", "typescript", "frontend"]
},
{
"id": "python-async",
"name": "Python Async Patterns",
"category": "coding",
"description": "Async/await best practices in Python",
"content": """Python async standards:
- Use async/await consistently, don't mix sync/async
- Use asyncio.gather() for concurrent operations
- Proper exception handling in async contexts
- Use async context managers (async with)
- Avoid blocking calls in async functions
- Use httpx over requests for async HTTP
- Timeout all async operations""",
"tags": ["python", "async", "asyncio"]
},
{
"id": "api-design",
"name": "REST API Design",
"category": "coding",
"description": "RESTful API design patterns",
"content": """API design standards:
- Use nouns for resources, not verbs
- Proper HTTP methods (GET/POST/PUT/DELETE)
- Return appropriate status codes
- Version APIs (/api/v1/)
- Use query params for filtering, sorting
- Pagination with limit/offset or cursor
- Consistent error response format
- Rate limiting headers""",
"tags": ["api", "rest", "backend"]
},
{
"id": "valheim-server",
"name": "Valheim Server Setup",
"category": "gameserver",
"description": "Valheim dedicated server configuration",
"content": """Valheim server standards:
- Run in Docker with persistent volumes
- Backup world files regularly
- Set -public 0 for private servers
- Configure admin list properly
- Monitor RAM usage (2-4GB typical)
- Use server sync for crossplay
- Restart nightly for memory leaks""",
"tags": ["valheim", "gaming", "docker"]
},
{
"id": "minecraft-server",
"name": "Minecraft Server Setup",
"category": "gameserver",
"description": "Minecraft server configuration patterns",
"content": """Minecraft server standards:
- Use PaperMC for performance
- Pre-generate world chunks
- Configure view-distance appropriately (6-10)
- Use Aikar's flags for JVM optimization
- Regular backups with rotation
- Whitelist for private servers
- Monitor TPS and chunk loading""",
"tags": ["minecraft", "gaming", "java"]
},
{
"id": "git-commits",
"name": "Git Commit Standards",
"category": "coding",
"description": "Commit message conventions",
"content": """Commit message format:
- Conventional Commits (feat:, fix:, chore:, etc.)
- Imperative mood ("add feature" not "added feature")
- First line max 50 chars
- Blank line before body
- Body wraps at 72 chars
- Reference issues/PRs when applicable""",
"tags": ["git", "workflow", "documentation"]
},
{
"id": "dnd-npc-creation",
"name": "D&D NPC Creation",
"category": "dnd",
"description": "Standards for creating memorable non-player characters",
"content": """NPC creation guidelines:
- Give each NPC one distinctive trait (speech pattern, habit, appearance)
- Motivation > backstory - what do they want NOW?
- Tie NPCs to locations or other NPCs (web of connections)
- Use the "Three Details" rule: name, appearance, mannerism
- Avoid stereotypes; subvert expectations thoughtfully
- Consider how they change over time (arcs aren't just for PCs)""",
"tags": ["dnd", "npc", "character", "writing"]
},
{
"id": "dnd-plot-hooks",
"name": "D&D Plot Hook Generation",
"category": "dnd",
"description": "Patterns for compelling quest seeds and story hooks",
"content": """Effective plot hooks include:
- Personal connection to a PC's backstory
- Urgent need (timer = engagement)
- Moral ambiguity (not just "kill monsters")
- Mystery with multiple potential solutions
- Hook should lead to 3+ possible directions
- Include a "weird" element to spark curiosity
- Avoid railroading; present options, not one path""",
"tags": ["dnd", "plot", "quest", "writing"]
},
{
"id": "homelab-backup-strategy",
"name": "Home Lab Backup Standards",
"category": "homelab",
"description": "Reliable backup patterns for self-hosted services",
"content": """Backup best practices:
- 3-2-1 rule: 3 copies, 2 media types, 1 offsite
- Use Borg/Restic with deduplication and encryption
- Test restores quarterly (backup is worthless without verification)
- Backup databases with point-in-time recovery (WAL for Postgres)
- Store backups on different physical disks than production
- Automate with systemd timers or cron, monitor failures
- Document restore procedures in runbooks""",
"tags": ["backup", "borg", "restic", "disaster-recovery"]
},
{
"id": "homelab-monitoring",
"name": "Home Lab Monitoring Stack",
"category": "homelab",
"description": "Prometheus + Grafana + Alertmanager setup patterns",
"content": """Monitoring standards:
- Prometheus scrapes metrics from all services (expose /metrics endpoint)
- Grafana dashboards for: system resources, app metrics, business KPIs
- Alertmanager with tiered alerts: info/warning/critical
- Use node_exporter for host metrics, docker_exporter for containers
- Retention: 30 days for warnings, 90 days for critical, 1 year for compliance
- Set up blackbox exporters for external uptime monitoring
- Document runbooks for each critical alert""",
"tags": ["monitoring", "prometheus", "grafana", "observability"]
},
{
"id": "python-testing-pytest",
"name": "Python Testing with pytest",
"category": "coding",
"description": "Comprehensive pytest patterns and practices",
"content": """Testing standards:
- Use pytest fixtures with function scope for isolation
- Test one behavior per test function (single responsibility)
- Use descriptive test names that explain the expectation
- Mock external services (HTTP, DB) with pytest-mock
- Parameterize tests for multiple input combinations
- Aim for 80%+ coverage, but prioritize critical paths
- Use hypothesis for property-based testing on complex logic""",
"tags": ["python", "testing", "pytest", "tdd"]
},
{
"id": "docker-security",
"name": "Docker Security Hardening",
"category": "security",
"description": "Security best practices for containerized applications",
"content": """Docker security checklist:
- Use distroless or alpine base images (minimal attack surface)
- Run as non-root user (USER directive in Dockerfile)
- Scan images with trivy or grype in CI
- Use read-only filesystems where possible (volumes for writes)
- Drop capabilities you don't need (--cap-drop ALL, then add back)
- Never store secrets in images - use Docker secrets or env files
- Keep base images updated (automate with Renovate/Dependabot)""",
"tags": ["docker", "security", "hardening"]
}
]
CONVENTIONS = [
{
"id": "home-server-conventions",
"project_path": "/opt/home-server",
"name": "Home Server Standards",
"content": """All home server deployments:
- Docker Compose for all services
- Traefik for reverse proxy
- Health checks on all containers
- Centralized logging
- Automated backups
- Resource limits defined""",
"auto_inject": True
}
]
SNIPPETS = [
{
"id": "docker-healthcheck",
"name": "Docker Health Check Template",
"language": "yaml",
"category": "docker",
"content": """healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s""",
"tags": ["docker", "health", "template"]
},
{
"id": "traefik-labels",
"name": "Traefik Docker Labels",
"language": "yaml",
"category": "docker",
"content": """labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`myapp.example.com`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
- "traefik.http.services.myapp.loadbalancer.server.port=8080""",
"tags": ["traefik", "labels", "template"]
}
]
def seed():
with httpx.Client(timeout=30.0) as client:
print("Seeding skills...")
for skill in SKILLS:
try:
response = client.post(f"{BASE_URL}/skills", json=skill)
if response.status_code == 200:
print(f"{skill['id']}")
elif response.status_code == 400:
print(f" ~ {skill['id']} (already exists)")
else:
print(f"{skill['id']}: {response.text}")
except Exception as e:
print(f"{skill['id']}: {e}")
print("\nSeeding conventions...")
for convention in CONVENTIONS:
try:
response = client.post(f"{BASE_URL}/conventions", json=convention)
if response.status_code == 200:
print(f"{convention['id']}")
elif response.status_code == 400:
print(f" ~ {convention['id']} (already exists)")
else:
print(f"{convention['id']}: {response.text}")
except Exception as e:
print(f"{convention['id']}: {e}")
print("\nSeeding snippets...")
for snippet in SNIPPETS:
try:
response = client.post(f"{BASE_URL}/snippets", json=snippet)
if response.status_code == 200:
print(f"{snippet['id']}")
elif response.status_code == 400:
print(f" ~ {snippet['id']} (already exists)")
else:
print(f"{snippet['id']}: {response.text}")
except Exception as e:
print(f"{snippet['id']}: {e}")
print("\nDone! Check http://helm:8675/docs")
if __name__ == "__main__":
seed()