diff --git a/CLAUDE.md b/CLAUDE.md index e8b9c21..97ca754 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,6 +51,19 @@ python manage.py dbshell python manage.py check_db ``` +### Agent Management (File-Based System) +```bash +# Agents are managed via JSON files - no commands needed! +# Simply add/edit JSON files in agents/configs/agents/ + +# View agent statistics +python -c " +from agents.services import AgentFileService +stats = AgentFileService.get_agent_stats() +print('Agent Stats:', stats) +" +``` + ### Testing ```bash # Run Django tests @@ -98,15 +111,17 @@ gunicorn netcop_hub.wsgi:application ### Apps Structure - **authentication/**: Custom user model, email verification, password reset - **core/**: Homepage, error handlers, utility functions -- **agents/**: Database-driven agent system (marketplace, execution, models, REST API) +- **agents/**: File-based agent system (marketplace, execution history, REST API for executions) - **wallet/**: Stripe payments, wallet management, transactions ### Agent System (agents app) **Key Files:** -- `agents/models.py`: Agent, AgentCategory, AgentExecution, ChatSession models +- `agents/services.py`: AgentFileService - file-based agent management +- `agents/configs/agents/`: JSON agent configuration files +- `agents/configs/categories/`: JSON category configuration files +- `agents/models.py`: AgentExecution, ChatSession models (execution history) - `agents/views.py`: Dual integration systems and web interface views - `agents/templates/agents/`: Dynamic agent templates and marketplace -- `agents/management/commands/`: Agent creation and management commands - `templates/career_navigator.html`: Direct access form template **Dual Integration Systems:** @@ -168,10 +183,11 @@ For detailed agent information and creation instructions, see `docs/AGENT_CREATI ``` ### Key Components -**Agent Configuration (Database-driven):** -- All agent metadata stored in database (pricing, descriptions, webhooks) +**Agent Configuration (File-driven):** +- All agent metadata stored in JSON files (pricing, descriptions, webhooks) - JSON form schemas for dynamic form generation -- Easy to add new agents via management commands or admin interface +- Instant agent creation by adding JSON files (no commands needed) +- Automatic database sync for foreign key compatibility **Templates:** - `templates/base.html`: Main layout with navigation @@ -184,8 +200,8 @@ For comprehensive agent creation instructions, see **`docs/AGENT_CREATION.md`**. **Quick Summary:** 1. Create JSON config in `agents/configs/agents/your-agent-name.json` -2. Run `python manage.py populate_agents` -3. Agent appears in marketplace automatically +2. Git push (or restart server locally) +3. Agent appears in marketplace automatically - no commands needed! The platform supports 2 agent types: - **Webhook Agents** - N8N integration with dynamic forms @@ -240,7 +256,7 @@ The platform supports 2 agent types: - **8 agents** confirmed working and tested (4 webhook + 4 direct access) - **6 categories** with clean, logical organization - **Dual integration architecture** with clear separation and documentation -- **Streamlined agent creation** via JSON configs + `populate_agents` command +- **Streamlined agent creation** via JSON configs (instant file-based loading) - **Scalable architecture** ready for 100+ agents **Current Agents:** @@ -249,13 +265,13 @@ The platform supports 2 agent types: **Latest Changes:** - **Added SWOT Analysis Expert** with proper category assignment (analysis) -- **Streamlined agent creation process** to use only JSON + `populate_agents` +- **Streamlined agent creation process** to use only JSON files (instant loading) - **Separated documentation** into focused files (`docs/AGENT_CREATION.md`) - **Removed 10+ redundant management commands** for cleaner codebase - **Fixed marketplace consistency** and updated documentation **Architecture Status:** -- **Error-free agent creation** via JSON configuration approach +- **Error-free agent creation** via file-based JSON configuration - **Railway-ready deployment** with automatic agent population - **Consistent UI standards** across all marketplace components - **Comprehensive documentation** prevents common development mistakes @@ -263,7 +279,7 @@ The platform supports 2 agent types: **Future Development:** - **New agents** should follow patterns in `docs/AGENT_CREATION.md` - **Use existing categories first** to avoid unnecessary proliferation -- **JSON + populate_agents** is the only supported creation method +- **JSON file-based approach** is the only supported creation method --- Last updated: 2025-01-08 diff --git a/agents/management/commands/populate_agents.py b/agents/management/commands/populate_agents.py deleted file mode 100644 index 1ce4143..0000000 --- a/agents/management/commands/populate_agents.py +++ /dev/null @@ -1,142 +0,0 @@ -import json -import os -from pathlib import Path -from django.core.management.base import BaseCommand -from agents.models import AgentCategory, Agent - -class Command(BaseCommand): - help = 'Dynamically populate all agents and categories from JSON configuration files' - - def handle(self, *args, **options): - self.stdout.write(self.style.SUCCESS('🚀 Dynamically populating agents from configuration files...')) - self.stdout.write('') - - # Track creation statistics - categories_created = 0 - agents_created = 0 - config_base_path = Path(__file__).parent.parent.parent / 'configs' - - # Load and create categories - categories_file = config_base_path / 'categories' / 'categories.json' - if not categories_file.exists(): - self.stdout.write(self.style.ERROR(f'❌ Categories file not found: {categories_file}')) - return - - with open(categories_file, 'r', encoding='utf-8') as f: - categories_data = json.load(f) - - categories = {} - for category_data in categories_data: - category, created = AgentCategory.objects.get_or_create( - slug=category_data['slug'], - defaults={ - 'name': category_data['name'], - 'description': category_data['description'], - 'icon': category_data['icon'] - } - ) - categories[category_data['slug']] = category - if created: - categories_created += 1 - self.stdout.write(f'✅ Created category: {category.name}') - else: - self.stdout.write(f' Category exists: {category.name}') - - self.stdout.write('') - - # Load and create agents from JSON files - agents_dir = config_base_path / 'agents' - if not agents_dir.exists(): - self.stdout.write(self.style.ERROR(f'❌ Agents directory not found: {agents_dir}')) - return - - # Get all JSON files in agents directory - agent_files = list(agents_dir.glob('*.json')) - if not agent_files: - self.stdout.write(self.style.WARNING('⚠️ No agent configuration files found')) - return - - self.stdout.write(f'📁 Found {len(agent_files)} agent configuration files') - self.stdout.write('') - - # Process each agent configuration file - for agent_file in sorted(agent_files): - try: - with open(agent_file, 'r', encoding='utf-8') as f: - agent_data = json.load(f) - - # Validate required fields - required_fields = ['slug', 'name', 'category', 'price', 'agent_type'] - missing_fields = [field for field in required_fields if field not in agent_data] - if missing_fields: - self.stdout.write(self.style.ERROR(f'❌ Missing fields in {agent_file.name}: {missing_fields}')) - continue - - # Get category - category_slug = agent_data['category'] - if category_slug not in categories: - self.stdout.write(self.style.ERROR(f'❌ Unknown category "{category_slug}" in {agent_file.name}')) - continue - - category = categories[category_slug] - - # Create agent - agent, created = Agent.objects.get_or_create( - slug=agent_data['slug'], - defaults={ - 'name': agent_data['name'], - 'short_description': agent_data.get('short_description', ''), - 'description': agent_data.get('description', ''), - 'category': category, - 'price': agent_data['price'], - 'agent_type': agent_data['agent_type'], - 'form_schema': agent_data.get('form_schema'), - 'webhook_url': agent_data.get('webhook_url', ''), - 'access_url_name': agent_data.get('access_url_name', ''), - 'display_url_name': agent_data.get('display_url_name', '') - } - ) - - if created: - agents_created += 1 - system_type = agent_data.get('system_type', 'webhook') - self.stdout.write(f'✅ Created agent: {agent.name} ({system_type.title()})') - self.stdout.write(f' 💰 Price: {agent.price} AED') - self.stdout.write(f' 📁 Config: {agent_file.name}') - else: - self.stdout.write(f' Agent exists: {agent.name} (from {agent_file.name})') - - except json.JSONDecodeError as e: - self.stdout.write(self.style.ERROR(f'❌ Invalid JSON in {agent_file.name}: {e}')) - continue - except Exception as e: - self.stdout.write(self.style.ERROR(f'❌ Error processing {agent_file.name}: {e}')) - continue - - self.stdout.write('') - self.stdout.write(self.style.SUCCESS('🎉 Dynamic population completed successfully!')) - self.stdout.write('') - self.stdout.write(f'📊 Summary:') - self.stdout.write(f' Categories created: {categories_created}') - self.stdout.write(f' Agents created: {agents_created}') - self.stdout.write(f' Configuration files processed: {len(agent_files)}') - self.stdout.write('') - - # Final verification - total_categories = AgentCategory.objects.filter(is_active=True).count() - total_agents = Agent.objects.filter(is_active=True).count() - webhook_agents = Agent.objects.filter(is_active=True, access_url_name='').count() - direct_agents = Agent.objects.filter(is_active=True).exclude(access_url_name='').count() - - self.stdout.write(f'🔍 Final verification:') - self.stdout.write(f' Total categories: {total_categories}') - self.stdout.write(f' Total agents: {total_agents}') - self.stdout.write(f' Webhook agents: {webhook_agents}') - self.stdout.write(f' Direct access agents: {direct_agents}') - self.stdout.write('') - self.stdout.write(self.style.SUCCESS('✅ Database is now consistent and ready!')) - self.stdout.write('') - self.stdout.write('🚀 To add new agents:') - self.stdout.write(' 1. Create new JSON file in agents/configs/agents/') - self.stdout.write(' 2. Run this command again') - self.stdout.write(' 3. Agent will automatically appear in marketplace!') \ No newline at end of file diff --git a/agents/serializers.py b/agents/serializers.py index bd8ca65..2419c70 100644 --- a/agents/serializers.py +++ b/agents/serializers.py @@ -1,23 +1,9 @@ from rest_framework import serializers -from .models import Agent, AgentCategory, AgentExecution - -class AgentCategorySerializer(serializers.ModelSerializer): - class Meta: - model = AgentCategory - fields = ['id', 'name', 'slug', 'description', 'icon'] - -class AgentSerializer(serializers.ModelSerializer): - category = AgentCategorySerializer(read_only=True) - - class Meta: - model = Agent - fields = [ - 'id', 'name', 'slug', 'short_description', 'description', - 'category', 'price', 'form_schema', 'created_at' - ] +from .models import AgentExecution class AgentExecutionSerializer(serializers.ModelSerializer): - agent = AgentSerializer(read_only=True) + # Note: agent field will contain the database Agent record for foreign key compatibility + # The actual agent data comes from files via AgentFileService class Meta: model = AgentExecution diff --git a/agents/urls.py b/agents/urls.py index f625a3c..5c0aeb6 100644 --- a/agents/urls.py +++ b/agents/urls.py @@ -30,8 +30,6 @@ urlpatterns = [ path('api/chat/end/', views.end_chat_session, name='end_chat_session'), path('api/chat/export//', views.export_chat, name='export_chat'), - path('api/', views.agent_list, name='agent_list'), - path('api//', views.agent_detail, name='agent_detail_api'), # Generic direct access routes (must be before agent detail) path('/access/', views.direct_access_handler, name='direct_access_handler'), diff --git a/agents/views.py b/agents/views.py index 4204325..6701ae2 100644 --- a/agents/views.py +++ b/agents/views.py @@ -7,7 +7,6 @@ from django.shortcuts import get_object_or_404, render, redirect from django.utils import timezone from django.contrib.auth.decorators import login_required from django.contrib import messages -from django.db import models from .models import Agent, AgentExecution, ChatSession, ChatMessage from .serializers import AgentExecutionSerializer from .services import AgentFileService @@ -63,34 +62,7 @@ def validate_webhook_url(url): except Exception as e: raise ValueError(f"Invalid webhook URL: {str(e)}") -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def agent_list(request): - """List all active agents with optional category filtering""" - agents = AgentFileService.get_active_agents() - - category = request.GET.get('category') - if category: - agents = AgentFileService.get_agents_by_category(category) - - search = request.GET.get('search') - if search: - agents = AgentFileService.search_agents(search) - - paginator = PageNumberPagination() - paginator.page_size = 20 - result_page = paginator.paginate_queryset(agents, request) - # Return the data directly since we're working with dictionaries - return paginator.get_paginated_response(result_page) -@api_view(['GET']) -@permission_classes([IsAuthenticated]) -def agent_detail(request, slug): - """Get detailed agent information""" - agent = AgentFileService.get_agent_by_slug(slug) - if not agent or not agent.get('is_active', True): - return Response({'error': 'Agent not found'}, status=status.HTTP_404_NOT_FOUND) - return Response(agent) @api_view(['POST']) @permission_classes([IsAuthenticated])