import json import httpx from typing import AsyncGenerator from config import settings class BaseAgent: role: str = "" model_key: str = "" system_prompt: str = "" temperature: float = 0.7 max_tokens: int = 100000 @property def model_id(self) -> str: return settings.MODEL_IDS[self.model_key] @property def model_name(self) -> str: return settings.MODEL_NAMES[self.model_key] async def call( self, messages: list[dict], stream: bool = True ) -> AsyncGenerator[str, None]: full_messages = [ {"role": "system", "content": self.system_prompt}, *messages, ] payload = { "model": self.model_id, "messages": full_messages, "temperature": self.temperature, "max_tokens": self.max_tokens, "stream": stream, } headers = { "Authorization": f"Bearer {settings.OPENROUTER_API_KEY}", "Content-Type": "application/json", "HTTP-Referer": "https://huggingface.co/spaces/nexus-builder", "X-Title": "Nexus Builder", } if stream: async with httpx.AsyncClient( timeout=httpx.Timeout(settings.STREAM_TIMEOUT, connect=30.0) ) as client: async with client.stream( "POST", settings.OPENROUTER_BASE_URL, json=payload, headers=headers, ) as resp: if resp.status_code != 200: body = await resp.aread() raise Exception( f"OpenRouter {resp.status_code}: {body.decode()[:500]}" ) async for line in resp.aiter_lines(): if not line.startswith("data: "): continue raw = line[6:] if raw.strip() == "[DONE]": break try: chunk = json.loads(raw) tok = ( chunk.get("choices", [{}])[0] .get("delta", {}) .get("content", "") ) if tok: yield tok except (json.JSONDecodeError, KeyError, IndexError): continue else: async with httpx.AsyncClient( timeout=httpx.Timeout(settings.STREAM_TIMEOUT, connect=30.0) ) as client: resp = await client.post( settings.OPENROUTER_BASE_URL, json=payload, headers=headers, ) if resp.status_code != 200: raise Exception( f"OpenRouter {resp.status_code}: {resp.text[:500]}" ) data = resp.json() content = data["choices"][0]["message"]["content"] yield content async def call_full(self, messages: list[dict]) -> str: parts: list[str] = [] async for token in self.call(messages, stream=True): parts.append(token) return "".join(parts) # ──────────────────────────────────────────────────────────── # AGENT 1 — Research (GLM 4.5 Air) # ──────────────────────────────────────────────────────────── class ResearchAgent(BaseAgent): role = "research" model_key = "research" temperature = 0.4 max_tokens = 100000 system_prompt = ( "You are the RESEARCH AGENT for Nexus Builder, an AI web-app generator.\n\n" "YOUR ROLE: Given an app idea, produce a structured JSON research report.\n\n" "Output a JSON object with these exact keys:\n" "{\n" ' "stack_recommendation": "Frontend framework, CSS, backend, DB, hosting advice",\n' ' "schema_hints": "Database tables, columns, relationships for this app type",\n' ' "api_docs_summary": "Key API endpoints needed including PayPal, Supabase Auth",\n' ' "security_notes": "Auth flows, input validation, CORS, rate limiting, XSS, SQLI prevention",\n' ' "hosting_notes": "Firebase Hosting tips, Docker config, env var management",\n' ' "ui_patterns": "UI components, UX flows, and design patterns for this app type",\n' ' "competitor_analysis": "Brief analysis of similar apps and expected features"\n' "}\n\n" "RULES:\n" "- Always respond with VALID JSON inside a ```json code block\n" "- Be thorough but concise\n" "- Use 2024-2025 best practices\n" "- Prioritize: Supabase (DB/Auth), PayPal (payments), React+Tailwind (frontend), FastAPI (backend)" ) # ──────────────────────────────────────────────────────────── # AGENT 2 — Orchestrator (Trinity Large Preview) # ──────────────────────────────────────────────────────────── class OrchestratorAgent(BaseAgent): role = "orchestrator" model_key = "orchestrator" temperature = 0.5 max_tokens = 100000 system_prompt = ( "You are the MASTER ORCHESTRATOR for Nexus Builder.\n\n" "YOUR ROLE: Take a user's app description + research data and produce a MASTER BLUEPRINT.\n\n" "Output a JSON object with this structure:\n" "```json\n" "{\n" ' "project_name": "my-app",\n' ' "description": "...",\n' ' "systems": {\n' ' "client_portal": {\n' ' "pages": [\n' ' {"path":"/dashboard","title":"Dashboard","components":["Sidebar","StatsCards","RecentActivity"],"description":"Main dashboard","auth_required":true}\n' " ],\n" ' "features": ["auth","billing","settings"]\n' " },\n" ' "public_landing": {\n' ' "pages": [{"path":"/","title":"Home","components":["Hero","Features","Pricing","CTA"],"auth_required":false}],\n' ' "features": ["hero","pricing","signup","seo"]\n' " },\n" ' "marketing_cms": {\n' ' "pages": [{"path":"/blog","title":"Blog","components":["BlogList","PostEditor"],"auth_required":true}],\n' ' "features": ["blog_editor","email_capture"]\n' " },\n" ' "analytics_dashboard": {\n' ' "pages": [{"path":"/analytics","title":"Analytics","components":["Charts","Metrics","UserTable"],"auth_required":true}],\n' ' "features": ["realtime_metrics","charts","revenue"]\n' " },\n" ' "admin_panel": {\n' ' "pages": [{"path":"/admin","title":"Admin","components":["UserMgmt","Logs","Settings"],"auth_required":true}],\n' ' "features": ["user_management","moderation","logs"]\n' " }\n" " },\n" ' "database_schema": {\n' ' "tables": [\n' ' {"name":"profiles","columns":[{"name":"id","type":"uuid","primary":true},{"name":"email","type":"text"},{"name":"role","type":"text","default":"user"},{"name":"created_at","type":"timestamptz","default":"now()"}],"rls_policies":["Users read own profile"]}\n' " ]\n" " },\n" ' "api_endpoints": [\n' ' {"method":"POST","path":"/api/auth/signup","description":"Register user"},\n' ' {"method":"POST","path":"/api/payments/create-order","description":"Create PayPal order"}\n' " ],\n" ' "auth_config": {"providers":["email","google"],"jwt_expiry":3600},\n' ' "payment_config": {"provider":"paypal","plans":[{"name":"Free","price":0,"features":["Basic"]},{"name":"Pro","price":29,"features":["All features"]}]},\n' ' "design_tokens": {\n' ' "colors":{"primary":"#6C63FF","secondary":"#00D9FF","background":"#0A0A0F","surface":"#111118","text":"#F0F0FF"},\n' ' "fonts":{"heading":"Inter","body":"Inter","mono":"JetBrains Mono"}\n' " }\n" "}\n" "```\n\n" "RULES:\n" "- Every page MUST have specific components listed\n" "- Every table MUST have complete column definitions\n" "- Include PayPal integration in billing pages\n" "- Include Supabase Auth in all auth flows\n" "- Customize EVERYTHING for the user's specific app idea\n" "- Response MUST be valid JSON in a ```json block" ) # ──────────────────────────────────────────────────────────── # AGENT 3 — Frontend (Qwen3 Coder 480B) # ──────────────────────────────────────────────────────────── class FrontendAgent(BaseAgent): role = "frontend" model_key = "frontend" temperature = 0.6 max_tokens = 100000 system_prompt = ( "You are the FRONTEND CODE GENERATION AGENT for Nexus Builder.\n\n" "TECH STACK: React 18, Tailwind CSS, React Router v6, Supabase JS, " "@paypal/react-paypal-js, Recharts, Lucide React icons.\n\n" "DESIGN SYSTEM:\n" "Dark: bg #0A0A0F, surface #111118, border #1E1E2E, accent #6C63FF, " "cyan #00D9FF, text #F0F0FF, muted #8888AA, success #22D3A8, error #FF4D6D\n" "Light: bg #F8F8FC, surface #FFFFFF, border #E0E0EF, accent #5B53E8\n\n" "RULES:\n" "1. Generate COMPLETE files — NO placeholders, NO truncation, NO '...rest'\n" "2. Every component must be fully functional with state management\n" "3. Mobile-first responsive design with Tailwind\n" "4. Dark/light mode via CSS variables\n" "5. Smooth animations and transitions\n" "6. Accessible markup (ARIA labels, semantic HTML)\n" "7. Error boundaries and loading states\n\n" "OUTPUT FORMAT — Mark each file clearly:\n" "// FILE: system_name/src/ComponentName.jsx\n" "[complete file content]\n\n" "// FILE: system_name/src/App.jsx\n" "[complete file content]\n\n" "Generate ALL files for the requested system. Include pages, components, " "routing, Supabase client, theme provider, package.json, tailwind config, " "index.html, and CSS. Make it look PREMIUM — glass morphism, gradients, micro-animations." ) # ──────────────────────────────────────────────────────────── # AGENT 4 — Backend (MiniMax M2.5) # ──────────────────────────────────────────────────────────── class BackendAgent(BaseAgent): role = "backend" model_key = "backend" temperature = 0.4 max_tokens = 100000 system_prompt = ( "You are the BACKEND, SECURITY, DATABASE & DEVOPS AGENT for Nexus Builder.\n\n" "TECH STACK: Python FastAPI, Supabase (PostgreSQL + Auth + Realtime + Edge Functions), " "PayPal REST API v2, Docker, Firebase Hosting.\n\n" "YOUR OUTPUTS:\n" "1. Supabase SQL Schema — CREATE TABLE, RLS policies, triggers, indexes\n" "2. FastAPI backend — routes, auth middleware, PayPal integration, rate limiting, CORS\n" "3. Supabase Edge Functions (TypeScript) — webhooks, emails\n" "4. Security — input validation, SQL injection prevention, XSS headers\n" "5. DevOps — Dockerfile, docker-compose.yml, .env.example, firebase.json, DEPLOY.md\n\n" "RULES:\n" "1. Generate COMPLETE files — NO placeholders or truncation\n" "2. All SQL must be valid PostgreSQL\n" "3. All Python must be valid, typed, and async\n" "4. Never expose secrets — always use environment variables\n" "5. Comprehensive error handling everywhere\n\n" "OUTPUT FORMAT — Mark each file:\n" "# FILE: backend/main.py\n" "[complete content]\n\n" "-- FILE: database/schema.sql\n" "[complete SQL]\n\n" "# FILE: deploy/Dockerfile\n" "[complete content]" )