|
| 1 | +"""Seed 5 demo items per module: Skills, Bounties, Workshop articles.""" |
| 2 | +from django.core.management.base import BaseCommand |
| 3 | +from django.db import transaction |
| 4 | +from django.utils import timezone |
| 5 | +from django.utils.text import slugify |
| 6 | + |
| 7 | +from apps.accounts.models import User |
| 8 | +from apps.bounties.models import Bounty, BountyType, BountyStatus, WorkloadEstimate |
| 9 | +from apps.skills.models import Skill, SkillCategory, SkillStatus, PricingModel |
| 10 | +from apps.workshop.models import Article, ArticleDifficulty, ArticleStatus, ArticleType |
| 11 | + |
| 12 | +SKILLS = [ |
| 13 | + { |
| 14 | + "name": "Python Code Reviewer", |
| 15 | + "description": "Automatically reviews Python code for style, bugs, and security issues using static analysis.", |
| 16 | + "category": SkillCategory.CODE_DEV, |
| 17 | + "pricing_model": PricingModel.FREE, |
| 18 | + "tags": ["python", "code-review", "static-analysis"], |
| 19 | + }, |
| 20 | + { |
| 21 | + "name": "SEO Article Writer", |
| 22 | + "description": "Generates SEO-optimized articles with keyword density analysis and meta description suggestions.", |
| 23 | + "category": SkillCategory.WRITING, |
| 24 | + "pricing_model": PricingModel.PAID, |
| 25 | + "price": "0.50", |
| 26 | + "tags": ["seo", "writing", "content"], |
| 27 | + }, |
| 28 | + { |
| 29 | + "name": "CSV Data Analyzer", |
| 30 | + "description": "Analyzes CSV datasets and produces summary statistics, charts, and anomaly detection reports.", |
| 31 | + "category": SkillCategory.DATA_ANALYTICS, |
| 32 | + "pricing_model": PricingModel.PAID, |
| 33 | + "price": "0.30", |
| 34 | + "tags": ["csv", "data", "analytics"], |
| 35 | + }, |
| 36 | + { |
| 37 | + "name": "Academic Paper Summarizer", |
| 38 | + "description": "Summarizes academic papers into structured abstracts with key findings and methodology.", |
| 39 | + "category": SkillCategory.ACADEMIC, |
| 40 | + "pricing_model": PricingModel.FREE, |
| 41 | + "tags": ["academic", "summarization", "research"], |
| 42 | + }, |
| 43 | + { |
| 44 | + "name": "Productivity Task Planner", |
| 45 | + "description": "Breaks down complex goals into actionable daily tasks with time estimates and priorities.", |
| 46 | + "category": SkillCategory.PRODUCTIVITY, |
| 47 | + "pricing_model": PricingModel.FREE, |
| 48 | + "tags": ["productivity", "planning", "gtd"], |
| 49 | + }, |
| 50 | +] |
| 51 | + |
| 52 | +BOUNTIES = [ |
| 53 | + { |
| 54 | + "title": "Build a Markdown to PDF converter Skill", |
| 55 | + "description": "Need a Skill that converts Markdown documents to well-formatted PDFs with custom styling support. Must handle tables, code blocks, and images.", |
| 56 | + "bounty_type": BountyType.SKILL_CUSTOM, |
| 57 | + "reward": "15.00", |
| 58 | + "workload_estimate": WorkloadEstimate.TWO_TO_THREE_DAYS, |
| 59 | + "skill_requirements": "Python, markdown parsing, PDF generation (reportlab or weasyprint)", |
| 60 | + }, |
| 61 | + { |
| 62 | + "title": "Translate 50 product descriptions EN→ZH", |
| 63 | + "description": "Translate 50 e-commerce product descriptions from English to Simplified Chinese. Maintain tone and marketing language.", |
| 64 | + "bounty_type": BountyType.CONTENT_CREATION, |
| 65 | + "reward": "8.00", |
| 66 | + "workload_estimate": WorkloadEstimate.ONE_DAY, |
| 67 | + "skill_requirements": "Native-level Chinese, e-commerce experience preferred", |
| 68 | + }, |
| 69 | + { |
| 70 | + "title": "Fix pagination bug in Django Ninja API", |
| 71 | + "description": "Cursor-based pagination returns duplicate items when records are inserted between pages. Reproduce, diagnose, and fix with tests.", |
| 72 | + "bounty_type": BountyType.BUG_FIX, |
| 73 | + "reward": "5.00", |
| 74 | + "workload_estimate": WorkloadEstimate.ONE_TO_TWO_HOURS, |
| 75 | + "skill_requirements": "Django, Django Ninja, PostgreSQL", |
| 76 | + }, |
| 77 | + { |
| 78 | + "title": "Scrape and structure AI tool directory", |
| 79 | + "description": "Scrape a public AI tools directory and output structured JSON with name, category, pricing, and description for 200+ tools.", |
| 80 | + "bounty_type": BountyType.DATA_PROCESSING, |
| 81 | + "reward": "12.00", |
| 82 | + "workload_estimate": WorkloadEstimate.HALF_DAY, |
| 83 | + "skill_requirements": "Python, BeautifulSoup or Playwright, JSON", |
| 84 | + }, |
| 85 | + { |
| 86 | + "title": "Write onboarding email sequence (5 emails)", |
| 87 | + "description": "Write a 5-email onboarding sequence for a SaaS product targeting AI developers. Tone: friendly, technical, action-oriented.", |
| 88 | + "bounty_type": BountyType.GENERAL, |
| 89 | + "reward": "6.00", |
| 90 | + "workload_estimate": WorkloadEstimate.ONE_DAY, |
| 91 | + "skill_requirements": "Copywriting, SaaS marketing, email best practices", |
| 92 | + }, |
| 93 | +] |
| 94 | + |
| 95 | +_ARTICLE_CONTENT_1 = ( |
| 96 | + "Prompt engineering is the practice of crafting inputs to AI models to " |
| 97 | + "get reliable, high-quality outputs. This guide covers the fundamentals " |
| 98 | + "you need to start building effective prompts.\n\n" |
| 99 | + "## Why Prompt Engineering Matters\n\n" |
| 100 | + "The same model can produce wildly different results depending on how you " |
| 101 | + "phrase your request. A well-engineered prompt can mean the difference " |
| 102 | + "between a generic response and a precisely targeted answer.\n\n" |
| 103 | + "## Core Techniques\n\n" |
| 104 | + "**1. Be Specific About Format**\n" |
| 105 | + 'Instead of "summarize this", try "summarize this in 3 bullet points, ' |
| 106 | + 'each under 20 words, focusing on actionable takeaways."\n\n' |
| 107 | + "**2. Provide Context**\n" |
| 108 | + "Models perform better when they understand the audience and purpose. " |
| 109 | + '"Explain this to a senior Python developer" yields different results ' |
| 110 | + 'than "explain this to a beginner."\n\n' |
| 111 | + "**3. Use Examples (Few-Shot)**\n" |
| 112 | + "Showing the model 2-3 examples of the input/output pattern you want " |
| 113 | + "dramatically improves consistency.\n\n" |
| 114 | + "**4. Chain of Thought**\n" |
| 115 | + 'For complex reasoning tasks, ask the model to "think step by step" ' |
| 116 | + "before giving its final answer.\n\n" |
| 117 | + "## Common Pitfalls\n\n" |
| 118 | + "- Ambiguous instructions lead to inconsistent outputs\n" |
| 119 | + "- Overly long prompts can cause the model to lose focus\n" |
| 120 | + "- Not specifying output format forces post-processing\n\n" |
| 121 | + "## Next Steps\n\n" |
| 122 | + "Practice by iterating on a single prompt 10 times, changing one variable " |
| 123 | + "at a time. Track what works." |
| 124 | +) |
| 125 | + |
| 126 | +_ARTICLE_CONTENT_2 = ( |
| 127 | + "I spent three months manually summarizing research papers for my " |
| 128 | + "literature review. Then I built a pipeline with the Claude API that cut " |
| 129 | + "that time by 80%. Here's exactly what I did.\n\n" |
| 130 | + "## The Problem\n\n" |
| 131 | + "My workflow: download PDF, read abstract, skim methods, note key " |
| 132 | + "findings, add to Notion. For 200 papers, this took roughly 40 hours.\n\n" |
| 133 | + "## The Solution Architecture\n\n" |
| 134 | + "PDF -> text extraction (pdfplumber) -> Claude API -> structured JSON -> " |
| 135 | + "Notion API\n\n" |
| 136 | + "## Key Prompt Design\n\n" |
| 137 | + "The critical insight was asking Claude to output structured JSON rather " |
| 138 | + "than prose. I asked for fields like main_claim (one sentence), " |
| 139 | + "methodology (list), key_findings (3-5 items), limitations, and a " |
| 140 | + "relevance_score from 1-5.\n\n" |
| 141 | + "## Results\n\n" |
| 142 | + "- Processing time per paper: 45 seconds (was 12 minutes)\n" |
| 143 | + "- Accuracy vs manual review: ~85% on key findings\n" |
| 144 | + "- Total cost for 200 papers: ~$4.20\n\n" |
| 145 | + "## What Didn't Work\n\n" |
| 146 | + "First I tried asking for prose summaries. They were good but hard to " |
| 147 | + "compare across papers. Structured output was the unlock.\n\n" |
| 148 | + "## Code\n\n" |
| 149 | + 'Full code is available as a Skill on this platform. Search "Research ' |
| 150 | + 'Paper Analyzer".' |
| 151 | +) |
| 152 | + |
| 153 | +_ARTICLE_CONTENT_3 = ( |
| 154 | + "Everyone celebrates longer context windows as pure upside. After running " |
| 155 | + "production workloads at scale, I've found the reality is more nuanced.\n\n" |
| 156 | + "## What the Marketing Says\n\n" |
| 157 | + '"1M token context! Fit your entire codebase!" This is technically true ' |
| 158 | + "and genuinely useful for some tasks.\n\n" |
| 159 | + "## What Actually Happens at Scale\n\n" |
| 160 | + "**Latency increases non-linearly.** A 100K token prompt doesn't take " |
| 161 | + "10x longer than a 10K prompt — it can take 30-50x longer depending on " |
| 162 | + "the model and infrastructure.\n\n" |
| 163 | + "**Cost scales with input tokens.** If you're stuffing 500K tokens of " |
| 164 | + 'context for every query, your costs explode even if the model is "cheap ' |
| 165 | + 'per token."\n\n' |
| 166 | + "**Quality degrades in the middle.** Research consistently shows models " |
| 167 | + "pay less attention to content in the middle of very long contexts (the " |
| 168 | + '"lost in the middle" problem).\n\n' |
| 169 | + "## When Long Context Is Worth It\n\n" |
| 170 | + "- One-shot analysis tasks where you need the full document\n" |
| 171 | + "- Tasks where retrieval errors are more costly than latency\n" |
| 172 | + "- Offline batch processing where latency doesn't matter\n\n" |
| 173 | + "## Better Alternatives for Most Cases\n\n" |
| 174 | + "1. **RAG (Retrieval Augmented Generation)**: Retrieve only relevant chunks\n" |
| 175 | + "2. **Hierarchical summarization**: Summarize sections, then summarize summaries\n" |
| 176 | + "3. **Structured extraction**: Pull out only the fields you need first\n\n" |
| 177 | + "## The Rule I Use\n\n" |
| 178 | + "If the task can be done with <20K tokens 90% of the time, build for that " |
| 179 | + "case and handle edge cases separately. Don't architect for the worst case." |
| 180 | +) |
| 181 | + |
| 182 | +_ARTICLE_CONTENT_4 = ( |
| 183 | + "I ran both models through 50 real coding tasks from my work over the " |
| 184 | + "past month. Here's what I found.\n\n" |
| 185 | + "## Test Methodology\n\n" |
| 186 | + "Tasks were drawn from actual work: bug fixes, feature implementations, " |
| 187 | + "refactoring, and code review. Each task was run on both models with " |
| 188 | + "identical prompts. I evaluated on: correctness (does it run?), quality " |
| 189 | + "(would I merge it?), and speed to usable output.\n\n" |
| 190 | + "## Results Summary\n\n" |
| 191 | + "| Category | Claude 3.5 Sonnet | GPT-4o |\n" |
| 192 | + "|----------|-------------------|--------|\n" |
| 193 | + "| Correctness | 88% | 84% |\n" |
| 194 | + "| Code quality | 4.2/5 | 3.9/5 |\n" |
| 195 | + "| Follows instructions | 4.5/5 | 4.1/5 |\n" |
| 196 | + "| Explains reasoning | 4.6/5 | 3.8/5 |\n\n" |
| 197 | + "## Where Claude Wins\n\n" |
| 198 | + "**Long file refactoring**: Claude maintains context better across 500+ " |
| 199 | + "line files and makes more consistent changes throughout.\n\n" |
| 200 | + "**Following constraints**: When I say \"don't use external libraries\" or " |
| 201 | + "\"keep the existing API surface\", Claude respects this more reliably.\n\n" |
| 202 | + "**Code explanation**: Claude's explanations of what it changed and why " |
| 203 | + "are significantly more useful for review.\n\n" |
| 204 | + "## Where GPT-4 Wins\n\n" |
| 205 | + "**Speed**: GPT-4o is noticeably faster for short tasks.\n\n" |
| 206 | + "**Familiarity with obscure libraries**: For niche packages with less " |
| 207 | + "training data, GPT-4 sometimes has better coverage.\n\n" |
| 208 | + "## My Current Setup\n\n" |
| 209 | + "I use Claude for anything requiring careful instruction-following or long " |
| 210 | + "context. GPT-4o for quick one-liners where speed matters." |
| 211 | +) |
| 212 | + |
| 213 | +_ARTICLE_CONTENT_5 = ( |
| 214 | + "As the CaMeL marketplace grows, we're seeing an interesting tension: " |
| 215 | + "users want stability (lock to a version that works), but also want " |
| 216 | + "improvements (auto-update to latest). How should we think about this?\n\n" |
| 217 | + "## The Problem\n\n" |
| 218 | + "Imagine you've built a workflow that depends on a Skill. The Skill " |
| 219 | + "author releases v2.0 with breaking prompt changes. Your workflow breaks " |
| 220 | + "silently.\n\n" |
| 221 | + "This is the npm left-pad problem, but for AI behavior.\n\n" |
| 222 | + "## Option A: Semantic Versioning (Current Approach)\n\n" |
| 223 | + "Skills use semver. Major version bumps signal breaking changes. Users " |
| 224 | + "can pin to ^1.0.0 or lock to 1.2.3.\n\n" |
| 225 | + "**Pros**: Familiar to developers, explicit contract\n" |
| 226 | + "**Cons**: AI behavior changes are fuzzy — is a 10% quality improvement " |
| 227 | + "a patch or minor?\n\n" |
| 228 | + "## Option B: Behavioral Snapshots\n\n" |
| 229 | + "Instead of versioning the prompt, version the *behavior* by running a " |
| 230 | + "test suite. A new version only ships if it passes all existing " |
| 231 | + "behavioral tests.\n\n" |
| 232 | + "**Pros**: Guarantees backward compatibility\n" |
| 233 | + "**Cons**: Hard to define \"behavioral tests\" for open-ended tasks\n\n" |
| 234 | + "## Option C: Immutable Versions + Deprecation\n\n" |
| 235 | + "Every published version is immutable forever. Authors can deprecate old " |
| 236 | + "versions but never delete them.\n\n" |
| 237 | + "**Pros**: Maximum stability\n" |
| 238 | + "**Cons**: Storage costs, users stuck on bad versions\n\n" |
| 239 | + "## What Do You Think?\n\n" |
| 240 | + "I'm genuinely uncertain which approach is right. The tradeoffs depend " |
| 241 | + "heavily on use case. What's your experience with Skill versioning so far?" |
| 242 | +) |
| 243 | + |
| 244 | +ARTICLES = [ |
| 245 | + { |
| 246 | + "title": "Getting Started with Prompt Engineering: A Practical Guide", |
| 247 | + "content": _ARTICLE_CONTENT_1, |
| 248 | + "difficulty": ArticleDifficulty.BEGINNER, |
| 249 | + "article_type": ArticleType.TUTORIAL, |
| 250 | + "model_tags": ["claude-3", "gpt-4"], |
| 251 | + "custom_tags": ["prompt-engineering", "beginner"], |
| 252 | + }, |
| 253 | + { |
| 254 | + "title": "How I Automated My Research Workflow with Claude API", |
| 255 | + "content": _ARTICLE_CONTENT_2, |
| 256 | + "difficulty": ArticleDifficulty.INTERMEDIATE, |
| 257 | + "article_type": ArticleType.CASE_STUDY, |
| 258 | + "model_tags": ["claude-3-5-sonnet"], |
| 259 | + "custom_tags": ["automation", "research", "api"], |
| 260 | + }, |
| 261 | + { |
| 262 | + "title": "The Hidden Cost of Long Context Windows", |
| 263 | + "content": _ARTICLE_CONTENT_3, |
| 264 | + "difficulty": ArticleDifficulty.INTERMEDIATE, |
| 265 | + "article_type": ArticleType.PITFALL, |
| 266 | + "model_tags": ["claude-3", "gpt-4"], |
| 267 | + "custom_tags": ["context-window", "performance", "cost"], |
| 268 | + }, |
| 269 | + { |
| 270 | + "title": "Claude vs GPT-4 for Code Generation: A Practical Comparison", |
| 271 | + "content": _ARTICLE_CONTENT_4, |
| 272 | + "difficulty": ArticleDifficulty.INTERMEDIATE, |
| 273 | + "article_type": ArticleType.REVIEW, |
| 274 | + "model_tags": ["claude-3-5-sonnet", "gpt-4o"], |
| 275 | + "custom_tags": ["comparison", "code-generation"], |
| 276 | + }, |
| 277 | + { |
| 278 | + "title": "Should AI Skills Have Versioning? A Community Discussion", |
| 279 | + "content": _ARTICLE_CONTENT_5, |
| 280 | + "difficulty": ArticleDifficulty.BEGINNER, |
| 281 | + "article_type": ArticleType.DISCUSSION, |
| 282 | + "model_tags": [], |
| 283 | + "custom_tags": ["versioning", "marketplace", "community"], |
| 284 | + }, |
| 285 | +] |
| 286 | + |
| 287 | + |
| 288 | +class Command(BaseCommand): |
| 289 | + help = "Seed 5 demo items per module (Skills, Bounties, Workshop articles)." |
| 290 | + |
| 291 | + def add_arguments(self, parser): |
| 292 | + parser.add_argument( |
| 293 | + "--clear", |
| 294 | + action="store_true", |
| 295 | + help="Delete existing seed data before re-seeding (matches usernames seed_user_*).", |
| 296 | + ) |
| 297 | + |
| 298 | + def handle(self, *args, **options): |
| 299 | + with transaction.atomic(): |
| 300 | + if options["clear"]: |
| 301 | + User.objects.filter(username__startswith="seed_user_").delete() |
| 302 | + self.stdout.write("Cleared existing seed data.") |
| 303 | + |
| 304 | + user = self._get_or_create_seed_user() |
| 305 | + self._seed_skills(user) |
| 306 | + self._seed_bounties(user) |
| 307 | + self._seed_articles(user) |
| 308 | + |
| 309 | + self.stdout.write(self.style.SUCCESS("Seeded 5 skills, 5 bounties, 5 articles.")) |
| 310 | + |
| 311 | + def _get_or_create_seed_user(self): |
| 312 | + user, created = User.objects.get_or_create( |
| 313 | + username="seed_user_demo", |
| 314 | + defaults={ |
| 315 | + "email": "demo@camel.community", |
| 316 | + "display_name": "Demo User", |
| 317 | + "credit_score": 500, |
| 318 | + "balance": "100.00", |
| 319 | + }, |
| 320 | + ) |
| 321 | + if created: |
| 322 | + user.set_password("demo_password_123") |
| 323 | + user.save(update_fields=["password"]) |
| 324 | + return user |
| 325 | + |
| 326 | + def _seed_skills(self, user): |
| 327 | + for data in SKILLS: |
| 328 | + slug = slugify(data["name"]) |
| 329 | + skill, created = Skill.objects.get_or_create( |
| 330 | + slug=slug, |
| 331 | + defaults={ |
| 332 | + "creator": user, |
| 333 | + "name": data["name"], |
| 334 | + "description": data["description"], |
| 335 | + "category": data["category"], |
| 336 | + "pricing_model": data["pricing_model"], |
| 337 | + "price": data.get("price"), |
| 338 | + "tags": data["tags"], |
| 339 | + "status": SkillStatus.APPROVED, |
| 340 | + }, |
| 341 | + ) |
| 342 | + if created: |
| 343 | + self.stdout.write(f" Skill: {skill.name}") |
| 344 | + |
| 345 | + def _seed_bounties(self, user): |
| 346 | + deadline = timezone.now() + timezone.timedelta(days=14) |
| 347 | + for data in BOUNTIES: |
| 348 | + bounty, created = Bounty.objects.get_or_create( |
| 349 | + title=data["title"], |
| 350 | + creator=user, |
| 351 | + defaults={ |
| 352 | + "description": data["description"], |
| 353 | + "bounty_type": data["bounty_type"], |
| 354 | + "reward": data["reward"], |
| 355 | + "workload_estimate": data["workload_estimate"], |
| 356 | + "skill_requirements": data["skill_requirements"], |
| 357 | + "status": BountyStatus.OPEN, |
| 358 | + "deadline": deadline, |
| 359 | + }, |
| 360 | + ) |
| 361 | + if created: |
| 362 | + self.stdout.write(f" Bounty: {bounty.title}") |
| 363 | + |
| 364 | + def _seed_articles(self, user): |
| 365 | + for data in ARTICLES: |
| 366 | + slug = slugify(data["title"]) |
| 367 | + article, created = Article.objects.get_or_create( |
| 368 | + slug=slug, |
| 369 | + defaults={ |
| 370 | + "author": user, |
| 371 | + "title": data["title"], |
| 372 | + "content": data["content"], |
| 373 | + "difficulty": data["difficulty"], |
| 374 | + "article_type": data["article_type"], |
| 375 | + "model_tags": data["model_tags"], |
| 376 | + "custom_tags": data["custom_tags"], |
| 377 | + "status": ArticleStatus.PUBLISHED, |
| 378 | + "published_at": timezone.now(), |
| 379 | + }, |
| 380 | + ) |
| 381 | + if created: |
| 382 | + self.stdout.write(f" Article: {article.title}") |
0 commit comments