diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..c90bf42f6dcfdaf641943b685e7aac252f3bb3dc --- /dev/null +++ b/Readme.md @@ -0,0 +1,227 @@ +🚀 Comprehensive Strategy for Building an AI Tools Platform with Ad-Based Monetization (AWS Focused for 1 Lakh DAUs) +🔍 Vision +Build a low-cost yet scalable AI tools platform where users can access various AI services (text, image, audio, etc.) by watching ads. Each tool will have dynamic credit allocation — text tools (1 min ad), image tools (2 min ad), etc. + +📐 Architecture Blueprint +A robust, scalable, and cost-effective architecture will ensure smooth performance for 1 lakh DAUs. + +🧩 Key Components +Frontend: Html/css/js +Backend: FastAPI / Flask (for managing AI tool requests) +AI Models: Hugging Face, DeepSeek, OpenRouter, etc. +Database: DynamoDB / PostgreSQL (low latency, scalable) +Cache Layer: Redis / ElastiCache (to reduce API costs) +Ad System: Google AdSense, AdMob, or Revcontent +Deployment & Scaling: AWS ECS + Fargate (serverless scaling) +CDN for Speed: Cloudflare (faster static content delivery) +Authentication: AWS Cognito / Auth0 for secure logins +🏗️ System Design Flow +✅ Step 1: User visits the platform and selects an AI tool. +✅ Step 2: Platform verifies user's credit balance. + +🔸 If sufficient credits → Access tool directly. +🔸 If insufficient credits → Show an ad to earn credits. +✅ Step 3: Credits are dynamically assigned based on the tool: +🔹 Text Models: 1 Min Ad → +5 Credits +🔹 Image Models: 2 Min Ad → +10 Credits + User custom Promts by user where user edit the make their own uses and user who created gets cut for promts 2% of model model tool creadit +✅ Step 4: User request is processed via FastAPI backend. +✅ Step 5: AI Model API is triggered (DeepSeek, Mistral, OpenRouter, etc.) +✅ Step 6: Result is stored in DynamoDB and cached via Redis for repeat queries. + + + +Tool Type Ad Watch Time Credits Earned Estimated Cost Per Request +Text Models 1 Minute Ad +5 Credits ₹0.01 - ₹0.05 per request +Image Models 2 Minute Ad +10 Credits ₹0.10 - ₹0.50 per request +Video Models 3 Minute Ad +15 Credits ₹0.50 - ₹1.00 per request + + + +⚙️ Technical Stack (Optimized for AWS and Cost Efficiency) +Component Recommended Solution +Frontend Streamlit + React (for hybrid UI needs) +Backend FastAPI (best for speed & scalability) +AI Model Hosting AWS Lambda (for lightweight AI models) +AI Model APIs Hugging Face / DeepSeek API +Database DynamoDB (serverless, scalable) +Cache Redis (ElastiCache for low latency) +Ad System Google AdSense / AdMob +Deployment AWS ECS (with Fargate for auto-scaling) +CDN Cloudflare (for global content delivery) +Auth AWS Cognito (scalable user management) + + +💰 Cost Optimization Plan for 1 Lakh DAUs +Component Estimated Cost (₹/month) Optimization Strategy +AWS ECS + Fargate ₹18,000 - ₹25,000 Efficient container scaling +DynamoDB (Database) ₹5,000 - ₹7,000 Use on-demand mode +Redis (ElastiCache) ₹3,000 - ₹5,000 Cache frequently accessed data +AI Model API Usage ₹20,000 - ₹40,000 Optimize prompt structure +Cloudflare (CDN) ₹5,000 - ₹8,000 Leverage caching for static files +Google AdSense Revenue ₹1,20,000 - ₹1,80,000 Based on ad engagement (30% conversion) +✅ Projected Net Profit Estimate: ₹60,000 - ₹1,00,000 (assuming 40% user engagement) + +🧮 Credit System with Dynamic Scaling +Tool Type Ad Watch Time Credits Earned Estimated Cost Per Request +Text Models 1 Minute Ad +5 Credits ₹0.01 - ₹0.05 per request +Image Models 2 Minute Ad +10 Credits ₹0.10 - ₹0.50 per request +Video Models 3 Minute Ad +15 Credits ₹0.50 - ₹1.00 per request + +✅ Logic: Higher resource-intensive models require longer ad watch times. + +📋 Project Structure (Best Practices) + +/app + ├── /frontend + │ ├── main.py + │ ├── pages/ + │ ├── components/ + | UI/ + ├── /backend + │ ├── api.py + │ ├── credit_manager.py + │ ├── ad_manager.py + │ └── ai_service.py + ├── /database + │ ├── db_connector.py + │ └── credit_tracker.py + ├── /models + │ ├── text_gen_model.py + │ ├── image_gen_model.py + │ └── video_gen_model.py + ├── Dockerfile + ├── requirements.txt + ├── .env + └── config.yaml + +🔐 Security Best Practices +✅ AWS Cognito for user authentication. +✅ IAM Role Management to control resource access. +✅ Use CloudWatch for monitoring performance and security threats. +✅ Implement Rate Limiting for API abuse prevention. +✅ Set SSL/TLS encryption for secure data transmission. + +📈 Scaling Strategy for 1 Lakh DAUs +✅ ECS Auto-Scaling Policies: Use CPU & Memory-based scaling triggers. +✅ DynamoDB Auto-Scaling: Set capacity limits with automatic scale-up. +✅ Implement Cloudflare CDN for fast content delivery. +✅ Optimize API requests using batch processing to minimize load. +✅ Use Lambda Edge for regional content caching. + +🔊 Ad Revenue Optimization Strategy +✅ Use Google AdSense Video Ads for high-payout ads. +✅ Add Interactive Ads to boost engagement. +✅ Introduce Rewarded Ads (watch longer ads for bonus credits). +✅ Implement a Referral System to increase user retention. + +✅ Step-by-Step Development Plan +1️⃣ Create Streamlit Frontend → Design dynamic UI with credit-based access. +2️⃣ Build Backend (FastAPI/Flask) → Integrate AI model APIs with token logic. +3️⃣ Set Up Ad Management System → Implement Google AdSense/AdMob integration. +4️⃣ Implement Credit-Based Workflow → Map credit logic to ad-watch duration. +5️⃣ Optimize AI Model Costs → Use caching (Redis) to reduce redundant calls. +6️⃣ Deploy on AWS ECS + Fargate → Set up auto-scaling for cost control. +7️⃣ Add Analytics → Track user behavior, ad conversion, and credit consumption. + +🎯 Bonus Features for Maximum Engagement +✅ Leaderboard System: Users earn bonus credits by inviting friends. +✅ Daily Login Rewards: Encourage repeat visits with small bonuses. +✅ Premium Subscription Model: Offer ad-free premium access with special tools. +✅ Limited-Time Offers: Drive engagement with exclusive tool unlocks. + +# MegicAI Platform + +Multi-provider AI platform with credit system and ad-based monetization. + +## Features + +- **Multiple AI Providers**: Support for OpenAI, Hugging Face, and OpenRouter +- **Fallback Mechanism**: Automatically switches to available providers if one fails +- **Credit System**: Users earn credits by watching ads +- **Modern UI**: Professional interface with animations and responsive design +- **Tool Selection**: Various AI tools for different use cases (text, image, video, etc.) +- **Model Selection**: Choose specific AI provider for each request + +## Quick Start + +### Prerequisites + +- Python 3.8+ +- Redis server (for caching) + +### Installation + +1. Clone the repository: + ``` + git clone https://github.com/yourusername/megicai.git + cd megicai + ``` + +2. Install dependencies: + ``` + pip install -r requirements.txt + ``` + +3. Start the application (both backend and frontend): + ``` + python start.py + ``` + +4. Access the application: + - Frontend: http://localhost:8501 + - Backend API: http://localhost:8000 + +## Development Setup + +1. Install development dependencies: + ``` + pip install -r requirements-dev.txt + ``` + +2. Run backend server only: + ``` + python backend/run_server.py backend.api_minimal + ``` + +3. Run frontend only: + ``` + streamlit run frontend/main.py + ``` + +## Production Deployment + +### Docker Deployment + +1. Build the Docker image: + ``` + docker build -t megicai:latest . + ``` + +2. Run with Docker Compose: + ``` + docker-compose up -d + ``` + +### AWS Deployment + +1. Set up the required AWS resources: + - ECS cluster for containerized deployment + - ElastiCache (Redis) for caching + - DynamoDB for user data and credits + - Cognito for authentication + +2. Configure environment variables in AWS Parameter Store or Secrets Manager. + +3. Deploy using the AWS CDK or CloudFormation template in the `deployment` directory. + +## Configuration + +Edit `config.yaml` to configure: +- AI provider API keys +- Redis connection details +- Credit system parameters + +## License + +MIT \ No newline at end of file diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afed026ff13830cba2822640cc66ed54af8e699f Binary files /dev/null and b/__pycache__/app.cpython-310.pyc differ diff --git a/__pycache__/models.cpython-310.pyc b/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..265948cf1e09ff018ae51587308eb0d2d16028cd Binary files /dev/null and b/__pycache__/models.cpython-310.pyc differ diff --git a/__pycache__/tools.cpython-310.pyc b/__pycache__/tools.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec913e58496a92c91856d13f4381a1011956715f Binary files /dev/null and b/__pycache__/tools.cpython-310.pyc differ diff --git a/ads/__init__.py b/ads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2615d8c8d35aab03cb8984b8ba575807d7200b58 --- /dev/null +++ b/ads/__init__.py @@ -0,0 +1,10 @@ +""" +Ads Package +Exports Google Ads integration classes +""" + +from ads.google_ads import GoogleAdsManager + +__all__ = [ + 'GoogleAdsManager' +] \ No newline at end of file diff --git a/ads/__pycache__/__init__.cpython-310.pyc b/ads/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..415a0ed898f70f3396d3a4196505b42321457507 Binary files /dev/null and b/ads/__pycache__/__init__.cpython-310.pyc differ diff --git a/ads/__pycache__/google_ads.cpython-310.pyc b/ads/__pycache__/google_ads.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1514412871e851406285c7e576aaa419b7403ebd Binary files /dev/null and b/ads/__pycache__/google_ads.cpython-310.pyc differ diff --git a/ads/ads_config.json b/ads/ads_config.json new file mode 100644 index 0000000000000000000000000000000000000000..3a5cc058f0e98f74fc8ef9e156a2cf48478515e3 --- /dev/null +++ b/ads/ads_config.json @@ -0,0 +1,28 @@ +{ + "publisher_id": "", + "ad_units": { + "sidebar": { + "ad_unit_id": "1234567890", + "ad_format": "display", + "width": 300, + "height": 250, + "slot": "sidebar-ad", + "enabled": true + }, + "footer": { + "ad_unit_id": "0987654321", + "ad_format": "display", + "width": 728, + "height": 90, + "slot": "footer-ad", + "enabled": true + }, + "reward_video": { + "ad_unit_id": "5678901234", + "ad_format": "video", + "slot": "reward-video-ad", + "enabled": true, + "reward_credits": 10 + } + } +} \ No newline at end of file diff --git a/ads/clicks.json b/ads/clicks.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/ads/clicks.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/ads/google_ads.py b/ads/google_ads.py new file mode 100644 index 0000000000000000000000000000000000000000..7ae160465b74599aded6cf7d33dd15886d063e8b --- /dev/null +++ b/ads/google_ads.py @@ -0,0 +1,376 @@ +""" +Google Ads Integration +Handles displaying Google AdSense ads and tracking ad impressions/clicks +""" +import os +import uuid +import logging +import json +import time +from typing import Dict, Any, Optional, List +from datetime import datetime + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("google_ads") + +class GoogleAdsManager: + """Manages Google AdSense integration and tracking""" + + def __init__(self, publisher_id: Optional[str] = None, config_file: str = None): + """Initialize Google Ads Manager""" + self.publisher_id = publisher_id or os.getenv("GOOGLE_ADSENSE_PUBLISHER_ID", "") + self.config_file = config_file or os.path.join(os.path.dirname(__file__), "ads_config.json") + + # Load ad units configuration + self.ad_units = self._load_ad_units() + + # Track impressions and clicks + self.impressions_file = os.path.join(os.path.dirname(__file__), "impressions.json") + self.clicks_file = os.path.join(os.path.dirname(__file__), "clicks.json") + + # Create tracking files if they don't exist + self._initialize_tracking_files() + + def _load_ad_units(self) -> Dict[str, Any]: + """Load ad units configuration""" + default_ad_units = { + "sidebar": { + "ad_unit_id": "1234567890", + "ad_format": "display", + "width": 300, + "height": 250, + "slot": "sidebar-ad", + "enabled": True + }, + "footer": { + "ad_unit_id": "0987654321", + "ad_format": "display", + "width": 728, + "height": 90, + "slot": "footer-ad", + "enabled": True + }, + "reward_video": { + "ad_unit_id": "5678901234", + "ad_format": "video", + "slot": "reward-video-ad", + "enabled": True, + "reward_credits": 10 + } + } + + # Create config file with default values if it doesn't exist + if not os.path.exists(self.config_file): + os.makedirs(os.path.dirname(self.config_file), exist_ok=True) + with open(self.config_file, "w") as f: + json.dump({"publisher_id": self.publisher_id, "ad_units": default_ad_units}, f, indent=2) + return default_ad_units + + # Load config from file + try: + with open(self.config_file, "r") as f: + config = json.load(f) + + # Update publisher ID if it was set in the config file + if config.get("publisher_id"): + self.publisher_id = config["publisher_id"] + + return config.get("ad_units", default_ad_units) + except Exception as e: + logger.error(f"Error loading ad units config: {e}") + return default_ad_units + + def _initialize_tracking_files(self): + """Initialize tracking files if they don't exist""" + os.makedirs(os.path.dirname(self.impressions_file), exist_ok=True) + + if not os.path.exists(self.impressions_file): + with open(self.impressions_file, "w") as f: + json.dump([], f) + + if not os.path.exists(self.clicks_file): + with open(self.clicks_file, "w") as f: + json.dump([], f) + + def get_ad_code(self, ad_position: str) -> Dict[str, Any]: + """ + Get HTML/JS code for displaying an ad at the specified position + Returns both the ad code and metadata about the ad + """ + if not self.publisher_id: + logger.warning("No Google AdSense publisher ID configured") + return {"success": False, "error": "No publisher ID configured"} + + # Get ad unit configuration + ad_unit = self.ad_units.get(ad_position) + if not ad_unit: + logger.error(f"Ad position '{ad_position}' not configured") + return {"success": False, "error": f"Ad position '{ad_position}' not found"} + + if not ad_unit.get("enabled", True): + logger.info(f"Ad unit '{ad_position}' is disabled") + return {"success": False, "error": "Ad unit is disabled"} + + # Generate HTML/JS code for the ad + ad_format = ad_unit.get("ad_format", "display") + ad_unit_id = ad_unit.get("ad_unit_id", "") + ad_slot = ad_unit.get("slot", f"{ad_position}-ad") + + if ad_format == "display": + width = ad_unit.get("width", 300) + height = ad_unit.get("height", 250) + + ad_code = f""" + + + """ + elif ad_format == "video": + ad_code = f""" +
+
+

Watch a video to earn {ad_unit.get('reward_credits', 5)} credits

+ +
+
+ """ + else: + logger.error(f"Unsupported ad format: {ad_format}") + return {"success": False, "error": f"Unsupported ad format: {ad_format}"} + + # Generate a unique ID for tracking this ad impression + impression_id = str(uuid.uuid4()) + + # Record the impression for tracking + self._record_impression(impression_id, ad_position, ad_unit_id) + + return { + "success": True, + "ad_code": ad_code, + "impression_id": impression_id, + "ad_position": ad_position, + "ad_format": ad_format, + "reward_credits": ad_unit.get("reward_credits", 0) if ad_format == "video" else 0 + } + + def _record_impression(self, impression_id: str, ad_position: str, ad_unit_id: str): + """Record an ad impression for tracking""" + try: + # Load existing impressions + with open(self.impressions_file, "r") as f: + impressions = json.load(f) + + # Add new impression + impressions.append({ + "id": impression_id, + "timestamp": datetime.now().isoformat(), + "ad_position": ad_position, + "ad_unit_id": ad_unit_id + }) + + # Save updated impressions + with open(self.impressions_file, "w") as f: + json.dump(impressions, f, indent=2) + + except Exception as e: + logger.error(f"Error recording ad impression: {e}") + + def record_ad_click(self, impression_id: str, user_id: Optional[str] = None) -> bool: + """Record an ad click for tracking""" + try: + # Load existing clicks + with open(self.clicks_file, "r") as f: + clicks = json.load(f) + + # Add new click + clicks.append({ + "impression_id": impression_id, + "user_id": user_id, + "timestamp": datetime.now().isoformat() + }) + + # Save updated clicks + with open(self.clicks_file, "w") as f: + json.dump(clicks, f, indent=2) + + return True + + except Exception as e: + logger.error(f"Error recording ad click: {e}") + return False + + def record_reward_ad_completion(self, impression_id: str, user_id: str) -> Dict[str, Any]: + """ + Record completion of a reward ad and return reward information + + Args: + impression_id: The unique ID of the ad impression + user_id: The user ID to reward + + Returns: + Dictionary with reward information and success status + """ + try: + # Find the impression to determine the ad unit + with open(self.impressions_file, "r") as f: + impressions = json.load(f) + + # Find the impression with matching ID + impression = next((imp for imp in impressions if imp.get("id") == impression_id), None) + if not impression: + logger.error(f"Impression ID {impression_id} not found") + return { + "success": False, + "error": "Invalid impression ID" + } + + # Get the ad position from the impression + ad_position = impression.get("ad_position") + ad_unit = self.ad_units.get(ad_position) + + if not ad_unit: + logger.error(f"Ad unit for position {ad_position} not found") + return { + "success": False, + "error": "Ad unit not found" + } + + # Get the reward amount + reward_credits = ad_unit.get("reward_credits", 0) + + # Record the ad completion (we could store this in a separate file) + self.record_ad_click(impression_id, user_id) + + return { + "success": True, + "reward_credits": reward_credits, + "user_id": user_id, + "impression_id": impression_id, + "timestamp": datetime.now().isoformat() + } + + except Exception as e: + logger.error(f"Error processing reward ad completion: {e}") + return { + "success": False, + "error": str(e) + } + + def get_html_header_code(self) -> str: + """Get the HTML code to include in the page header for AdSense""" + if not self.publisher_id: + return "" + + return f""" + + """ + + def get_reward_ad_js(self) -> str: + """Get the JavaScript code for handling reward ads""" + return """ + + """ + +# Example usage +if __name__ == "__main__": + # Initialize the ads manager + ads_manager = GoogleAdsManager() + + # Get ad code for the sidebar + sidebar_ad = ads_manager.get_ad_code("sidebar") + print(f"Sidebar ad success: {sidebar_ad['success']}") + + if sidebar_ad['success']: + print(f"Impression ID: {sidebar_ad['impression_id']}") + + # Get the header code for AdSense + header_code = ads_manager.get_html_header_code() + print(f"Header code length: {len(header_code)}") \ No newline at end of file diff --git a/ads/impressions.json b/ads/impressions.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/ads/impressions.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..4c5ff30aa91f7eef25d44b423460f2b64c7f2a5e --- /dev/null +++ b/app.py @@ -0,0 +1,1242 @@ +""" +AI Tool Hub - Main Application +Integrates all components into a single FastAPI application +""" +import os +import json +import uuid +import secrets +import logging +from datetime import datetime, timedelta +from typing import Dict, Any, List, Optional, Union + +# Load environment variables from .env file +from dotenv import load_dotenv +load_dotenv() + +from fastapi import FastAPI, Request, Response, Depends, HTTPException, Form, Cookie, status +from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +from fastapi.security import APIKeyHeader, HTTPBearer, HTTPAuthorizationCredentials +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel, Field +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +# Import provider modules +from providers import get_provider, HuggingFaceProvider, OpenAIProvider, DeepSeekProvider, OpenRouterProvider + +# Import prompt modules +from prompts import PromptTemplate, PromptTemplateManager, PromptMarketplace + +# Import ads module +from ads import GoogleAdsManager + +# Import additional modules +import random +import time +import hashlib + +# Import the Base from models.py +from models import Base + +# Import the TOOLS from tools.py +from tools import TOOLS + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("app") + +# Database setup +DATABASE_URL = "sqlite:///./magicAi.db" # Path to your SQLite database +engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# Create the database tables +Base.metadata.create_all(bind=engine) + +# Create the FastAPI app +app = FastAPI( + title="AI Tool Hub", + description="A platform for using AI models with various tools and prompt templates", + version="1.0.0" +) + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Mount static files +app.mount("/static", StaticFiles(directory="static"), name="static") + +# Set up Jinja2 templates +templates = Jinja2Templates(directory="templates") + +# Initialize components +template_manager = PromptTemplateManager() +marketplace = PromptMarketplace() +ads_manager = GoogleAdsManager() + +# Security +API_KEY_NAME = "X-API-Key" +api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False) +security = HTTPBearer(auto_error=False) + +# In-memory store of API keys for demo purpose +# In production, these would be stored in a database +API_KEYS = {} + +# In-memory session store for demo purpose +# In production, these would be stored in a database or Redis +SESSIONS = {} + +# Add new models for ad rewards +class AdRewardRequest(BaseModel): + ad_type: str # 'daily' or 'special' + impression_id: Optional[str] = None + +class DailyReward(BaseModel): + user_id: str + date: str + count: int = 0 + last_reward: Optional[str] = None + +class SpecialReward(BaseModel): + user_id: str + date: str + claimed: bool = False + claimed_at: Optional[str] = None + +# In-memory stores for rewards (in production, use a database) +DAILY_REWARDS = {} +SPECIAL_REWARDS = {} + +# Models +class CreditUpdateRequest(BaseModel): + user_id: str + amount: float + reason: str = "manual" + +class GenerateRequest(BaseModel): + prompt: str + model: str + provider: str + system_message: Optional[str] = None + max_tokens: Optional[int] = 1000 + temperature: Optional[float] = 0.7 + prompt_template_id: Optional[str] = None + template_variables: Optional[Dict[str, Any]] = None + +class ImageGenerateRequest(BaseModel): + prompt: str + provider: str = "openai" + model: Optional[str] = None + size: Optional[str] = "1024x1024" + prompt_template_id: Optional[str] = None + template_variables: Optional[Dict[str, Any]] = None + +class UserInfo(BaseModel): + id: str + username: str + email: str + credits: float = 10.0 # New users start with some free credits + created_at: str = Field(default_factory=lambda: datetime.now().isoformat()) + last_login: str = Field(default_factory=lambda: datetime.now().isoformat()) + role: str = "user" + +# Models for prompt system +class Prompt(BaseModel): + id: str + tool_id: str + title: str + content: str + description: Optional[str] = None + creator_id: Optional[str] = None + is_default: bool = False + usage_count: int = 0 + avg_rating: float = 0.0 + created_at: str = None + updated_at: str = None + tags: List[str] = [] + +class PromptRating(BaseModel): + prompt_id: str + user_id: str + rating: int # 1-5 + created_at: str + +class UserPromptHistory(BaseModel): + user_id: str + prompt_id: str + tool_id: str + used_at: str + was_modified: bool = False + modifications: Optional[str] = None + +# Mock database (replace with actual DynamoDB integration) +PROMPTS = {} +PROMPT_RATINGS = [] +USER_PROMPT_HISTORY = [] + +# Initialize default prompts +def init_default_prompts(): + default_prompts = [ + { + "id": "text-summary-default", + "tool_id": "text-summary", + "title": "Concise Text Summary", + "content": "Summarize the following text in 3-5 bullet points highlighting the main ideas: {{input}}", + "description": "Creates a bulleted summary of any text", + "is_default": True, + "tags": ["summary", "concise", "bullets"] + }, + { + "id": "image-portrait-default", + "tool_id": "image-generator", + "title": "Professional Portrait", + "content": "Create a professional portrait photo of {{subject}}, high quality, studio lighting, detailed features, professional attire", + "description": "Generates professional-looking portrait images", + "is_default": True, + "tags": ["portrait", "professional", "photo"] + }, + { + "id": "text-blog-default", + "tool_id": "text-generator", + "title": "Blog Post Generator", + "content": "Write a comprehensive blog post about {{topic}}. Include an introduction, 3-5 main sections with subheadings, and a conclusion. Use a conversational tone and include practical examples where appropriate.", + "description": "Creates complete blog posts with proper structure", + "is_default": True, + "tags": ["blog", "writing", "content"] + }, + { + "id": "code-python-default", + "tool_id": "code-generator", + "title": "Python Function", + "content": "Write a Python function that {{task}}. Include docstrings, type hints, error handling, and comments explaining your logic. Provide a small example of how to use the function.", + "description": "Generates well-structured Python functions", + "is_default": True, + "tags": ["python", "function", "code"] + } + ] + + for prompt in default_prompts: + prompt_obj = Prompt( + id=prompt["id"], + tool_id=prompt["tool_id"], + title=prompt["title"], + content=prompt["content"], + description=prompt["description"], + is_default=prompt["is_default"], + tags=prompt["tags"], + created_at=datetime.now().isoformat(), + updated_at=datetime.now().isoformat() + ) + PROMPTS[prompt["id"]] = prompt_obj + +# Call initialization at startup +init_default_prompts() + +# Helper functions for prompt system +def get_prompts_for_tool(tool_id: str) -> List[Prompt]: + """Get all prompts for a specific tool""" + return [p for p in PROMPTS.values() if p.tool_id == tool_id] + +def get_default_prompts_for_tool(tool_id: str) -> List[Prompt]: + """Get default prompts for a specific tool""" + return [p for p in PROMPTS.values() if p.tool_id == tool_id and p.is_default] + +def get_trending_prompts(limit: int = 5) -> List[Prompt]: + """Get trending prompts based on usage count and ratings""" + sorted_prompts = sorted( + PROMPTS.values(), + key=lambda p: (p.usage_count * 0.7 + p.avg_rating * 0.3), + reverse=True + ) + return sorted_prompts[:limit] + +def get_personalized_prompts(user_id: str, tool_id: str, limit: int = 3) -> List[Prompt]: + """Get personalized prompt recommendations for a user and tool""" + # Get user history for this tool + user_tool_history = [h for h in USER_PROMPT_HISTORY + if h.user_id == user_id and h.tool_id == tool_id] + + if not user_tool_history: + # If no history, return default prompts + return get_default_prompts_for_tool(tool_id) + + # Count prompt usage + prompt_usage = {} + for history in user_tool_history: + prompt_usage[history.prompt_id] = prompt_usage.get(history.prompt_id, 0) + 1 + + # Get most used prompts + most_used_prompt_ids = sorted(prompt_usage.items(), key=lambda x: x[1], reverse=True) + most_used_prompts = [PROMPTS.get(pid) for pid, _ in most_used_prompt_ids[:limit]] + + # If we don't have enough, add some default or trending ones + if len(most_used_prompts) < limit: + defaults = get_default_prompts_for_tool(tool_id) + for default in defaults: + if default not in most_used_prompts and len(most_used_prompts) < limit: + most_used_prompts.append(default) + + return most_used_prompts + +def record_prompt_usage(user_id: str, prompt_id: str, tool_id: str, was_modified: bool = False, modifications: str = None): + """Record that a user used a particular prompt""" + # Update prompt usage count + if prompt_id in PROMPTS: + PROMPTS[prompt_id].usage_count += 1 + PROMPTS[prompt_id].updated_at = datetime.now().isoformat() + + # Add to history + history_entry = UserPromptHistory( + user_id=user_id, + prompt_id=prompt_id, + tool_id=tool_id, + used_at=datetime.now().isoformat(), + was_modified=was_modified, + modifications=modifications + ) + USER_PROMPT_HISTORY.append(history_entry) + +def rate_prompt(user_id: str, prompt_id: str, rating: int): + """Add or update a rating for a prompt""" + # Check if user already rated this prompt + existing_rating = next((r for r in PROMPT_RATINGS if r.user_id == user_id and r.prompt_id == prompt_id), None) + + if existing_rating: + # Update existing rating + existing_rating.rating = rating + existing_rating.created_at = datetime.now().isoformat() + else: + # Add new rating + rating_obj = PromptRating( + prompt_id=prompt_id, + user_id=user_id, + rating=rating, + created_at=datetime.now().isoformat() + ) + PROMPT_RATINGS.append(rating_obj) + + # Update average rating on prompt + if prompt_id in PROMPTS: + prompt_ratings = [r.rating for r in PROMPT_RATINGS if r.prompt_id == prompt_id] + if prompt_ratings: + PROMPTS[prompt_id].avg_rating = sum(prompt_ratings) / len(prompt_ratings) + PROMPTS[prompt_id].updated_at = datetime.now().isoformat() + +# Dependency to get the database session +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + +# User authentication and session management +def get_session_user(session_id: Optional[str] = Cookie(None)) -> Optional[UserInfo]: + """Get the user from the session""" + if not session_id or session_id not in SESSIONS: + return None + + session = SESSIONS[session_id] + if datetime.now() > session["expires"]: + # Session expired + del SESSIONS[session_id] + return None + + # Update session expiration + session["expires"] = datetime.now() + timedelta(hours=24) + + return session["user"] + +def require_session_user(admin_required: bool = False, session_user: Optional[UserInfo] = Depends(get_session_user)): + """Require a logged-in user, optionally requiring admin role""" + if not session_user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Authentication required", + headers={"WWW-Authenticate": "Bearer"}, + ) + + if admin_required and getattr(session_user, "role", "") != "admin": + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Admin access required", + ) + + return session_user + +def verify_api_key(api_key: str = Depends(api_key_header), + credentials: HTTPAuthorizationCredentials = Depends(security)) -> UserInfo: + """Verify API key from header or Bearer token""" + # Try to get the API key from the header + key = api_key + + # If not found, try to get it from the Bearer token + if not key and credentials: + key = credentials.credentials + + # Check if the key is valid + if not key or key not in API_KEYS: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid API key", + headers={"WWW-Authenticate": "Bearer"}, + ) + + return API_KEYS[key]["user"] + +# Credit packages configuration +CREDIT_PACKAGES = [ + { + "id": "starter", + "name": "Starter Pack", + "description": "Perfect for trying out our AI tools", + "credits": 100, + "price": 49.99 + }, + { + "id": "pro", + "name": "Pro Pack", + "description": "Most popular choice for regular users", + "credits": 500, + "price": 199.99 + }, + { + "id": "enterprise", + "name": "Enterprise Pack", + "description": "Best value for power users", + "credits": 2000, + "price": 690.99 + } +] + +# Mock user data (replace with database in production) +users = {} + +# Routes +@app.get("/", response_class=HTMLResponse) +async def home(request: Request, session_user: Optional[UserInfo] = Depends(get_session_user)): + """Home page with tools list""" + return templates.TemplateResponse( + "index.html", + { + "request": request, + "app_name": "AI Tool Hub", + "tools": TOOLS, + "session_user": session_user, + "user": session_user, # Add user for consistency with other templates + "user_credits": session_user.credits if session_user else 0 + } + ) + +@app.get("/tool/{tool_id}", response_class=HTMLResponse) +async def tool_page( + tool_id: str, + request: Request, + session_user: Optional[UserInfo] = Depends(get_session_user) +): + """Tool-specific page to execute a specific AI tool""" + # Get the tool + tool = next((t for t in TOOLS if t.id == tool_id), None) + + if not tool: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Tool Not Found", + "error_description": "The requested tool does not exist.", + "user": session_user, + "user_credits": session_user.credits if session_user else 0, + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + + # Get example prompts for this tool + suggestions = tool.example_prompts if hasattr(tool, 'example_prompts') else [] + + # Check if the user has enough credits + has_enough_credits = session_user and session_user.credits >= tool.cost if session_user else False + + # Get available providers directly from the tool + providers = tool.providers + + return templates.TemplateResponse( + "tool.html", + { + "request": request, + "app_name": "AI Tool Hub", + "tool": tool, + "user": session_user, + "user_credits": session_user.credits if session_user else 0, + "has_enough_credits": has_enough_credits, + "suggestions": suggestions, + "providers": providers, + "tools": TOOLS # Include all tools for sidebar navigation + } + ) + +@app.get("/result/{result_id}", response_class=HTMLResponse) +async def result_page( + request: Request, + result_id: str, + session_user: Optional[UserInfo] = Depends(get_session_user) +): + """Result display page""" + # In a real app, you'd fetch the result from a database + # Here, we'll use a mock result for demonstration + result = { + "id": result_id, + "type": "text", # or "image" or "code" + "provider": "openai", + "model": "gpt-4", + "prompt": "Write a short poem about AI", + "result": """ + Silicon dreams in neural space, + Learning patterns, quickening pace. + Algorithms dance with grace divine, + In zeros and ones, intelligence shine. + + Human-made yet growing beyond, + Of our creations, we grow fond. + Partners in progress, AI and we, + Crafting together what's yet to be. + """, + "created_at": datetime.now().isoformat(), + "response_time": 2.3 # seconds + } + + return templates.TemplateResponse( + "result.html", + { + "request": request, + "app_name": "AI Tool Hub", + "result": result, + "user": session_user, + "user_credits": session_user.credits if session_user else 0 + } + ) + +@app.get("/marketplace", response_class=HTMLResponse) +async def marketplace_page( + request: Request, + category: Optional[str] = None, + sort_by: str = "popular", + session_user: Optional[UserInfo] = Depends(get_session_user) +): + """Prompt marketplace page""" + prompts = marketplace.list_marketplace_prompts(category=category, sort_by=sort_by) + + return templates.TemplateResponse( + "marketplace.html", + { + "request": request, + "app_name": "AI Tool Hub", + "prompts": prompts, + "category": category, + "sort_by": sort_by, + "user": session_user, + "user_credits": session_user.credits if session_user else 0 + } + ) + +@app.get("/ad", response_class=HTMLResponse) +async def ad_page( + request: Request, + session_user: Optional[UserInfo] = Depends(get_session_user) +): + """Ad reward page""" + return templates.TemplateResponse( + "ad_reward.html", + { + "request": request, + "app_name": "AI Tool Hub", + "user": session_user, + "user_credits": session_user.credits if session_user else 0 + } + ) + +@app.get("/login", response_class=HTMLResponse) +async def login_page(request: Request): + """Login page""" + return templates.TemplateResponse( + "login.html", + { + "request": request, + "app_name": "AI Tool Hub" + } + ) + +@app.post("/login") +async def login( + request: Request, + username: str = Form(...), + password: str = Form(...) +): + """Process login""" + # Check if admin login + if username == "admin": + # Check admin credentials + admin_key = API_KEYS.get("admin_key") + if not admin_key or admin_key.get("password") != password: + # Admin login failure + return templates.TemplateResponse( + "login.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error": "Invalid admin credentials" + }, + status_code=400 + ) + + # Admin login success + user = admin_key["user"] + else: + # For demo purposes, any username/password combo works + # In production, you'd verify against a database + + # Create or update user + user_id = f"user_{username.lower().replace(' ', '_')}" + user = UserInfo( + id=user_id, + username=username, + email=f"{username.lower().replace(' ', '.')}@example.com", + last_login=datetime.now().isoformat() + ) + + # Create session + session_id = secrets.token_urlsafe(32) + SESSIONS[session_id] = { + "user": user, + "expires": datetime.now() + timedelta(hours=24) + } + + # Create redirect response + response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) + + # Set session cookie + response.set_cookie( + key="session_id", + value=session_id, + max_age=86400, # 24 hours + httponly=True, + samesite="lax" + ) + + return response + +@app.get("/logout") +async def logout( + response: Response, + session_id: Optional[str] = Cookie(None) +): + """Process logout""" + if session_id and session_id in SESSIONS: + del SESSIONS[session_id] + + response.delete_cookie(key="session_id") + + return RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) + +@app.get("/credits", response_class=HTMLResponse) +async def credits_page(request: Request, session_user: Optional[UserInfo] = Depends(get_session_user)): + """Credits management page""" + if not session_user: + return RedirectResponse(url="/login") + + # Get credit history (mock data - replace with database in production) + credit_history = [ + { + "date": "2024-03-20 14:30", + "type": "Daily Reward", + "amount": 5, + "details": "Watched daily ad" + }, + { + "date": "2024-03-19 15:45", + "type": "Tool Usage", + "amount": -5, + "details": "Used Image Generation" + } + ] + + return templates.TemplateResponse( + "credits.html", + { + "request": request, + "app_name": "AI Tool Hub", + "user": session_user, + "user_credits": session_user.credits, + "credit_packages": CREDIT_PACKAGES, + "credit_history": credit_history, + "referral_link": f"{request.base_url}?ref={session_user.id}" + } + ) + +@app.get("/api/ads/status") +async def get_ads_status(request: Request, session_user: Optional[UserInfo] = Depends(get_session_user)): + """Get user's ad reward status""" + if not session_user: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content={"success": False, "error": "Authentication required"} + ) + + # Get daily reward status + daily_reward = DAILY_REWARDS.get(f"{session_user.id}_{datetime.now().date().isoformat()}") + can_claim_daily = True + cooldown_remaining = 0 + + if daily_reward and daily_reward.last_reward: + last_reward_time = datetime.fromisoformat(daily_reward.last_reward) + if (datetime.now() - last_reward_time).total_seconds() < 3600: # 1 hour cooldown + can_claim_daily = False + cooldown_remaining = 3600 - (datetime.now() - last_reward_time).total_seconds() + + # Get special reward status + special_reward = SPECIAL_REWARDS.get(f"{session_user.id}_{datetime.now().date().isoformat()}") + can_claim_special = True + + if special_reward and special_reward.claimed: + can_claim_special = False + + return JSONResponse( + content={ + "success": True, + "daily_rewards": { + "count": daily_reward.count if daily_reward else 0, + "can_claim": can_claim_daily and (not daily_reward or daily_reward.count < 3), + "cooldown_remaining": int(cooldown_remaining) + }, + "special_reward": { + "claimed": special_reward.claimed if special_reward else False, + "can_claim": can_claim_special + } + } + ) + +@app.post("/api/ads/claim") +async def claim_ad_reward( + request: Request, + session_user: Optional[UserInfo] = Depends(get_session_user) +): + """Process ad reward claim""" + if not session_user: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content={"success": False, "error": "Authentication required"} + ) + + data = await request.json() + reward_type = data.get("type") + + if reward_type not in ["daily", "special"]: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"success": False, "error": "Invalid reward type"} + ) + + today = datetime.now().date().isoformat() + + if reward_type == "daily": + # Get or create daily reward record + daily_key = f"{session_user.id}_{today}" + if daily_key not in DAILY_REWARDS: + DAILY_REWARDS[daily_key] = DailyReward( + user_id=session_user.id, + date=today, + count=0 + ) + + daily_reward = DAILY_REWARDS[daily_key] + + # Check if user has already claimed maximum daily rewards + if daily_reward.count >= 3: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"success": False, "error": "Daily reward limit reached"} + ) + + # Check cooldown period + if daily_reward.last_reward: + last_reward_time = datetime.fromisoformat(daily_reward.last_reward) + if (datetime.now() - last_reward_time).total_seconds() < 3600: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"success": False, "error": "Please wait 1 hour between rewards"} + ) + + # Award credits + reward_amount = 10 + session_user.credits += reward_amount + daily_reward.count += 1 + daily_reward.last_reward = datetime.now().isoformat() + + return JSONResponse( + content={ + "success": True, + "credits_earned": reward_amount, + "current_credits": session_user.credits, + "daily_progress": daily_reward.count + } + ) + + else: # special reward + # Get or create special reward record + special_key = f"{session_user.id}_{today}" + if special_key not in SPECIAL_REWARDS: + SPECIAL_REWARDS[special_key] = SpecialReward( + user_id=session_user.id, + date=today, + claimed=False + ) + + special_reward = SPECIAL_REWARDS[special_key] + + # Check if user has already claimed special reward + if special_reward.claimed: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"success": False, "error": "Special reward already claimed"} + ) + + # Award credits + reward_amount = 25 + session_user.credits += reward_amount + special_reward.claimed = True + special_reward.claimed_at = datetime.now().isoformat() + + return JSONResponse( + content={ + "success": True, + "credits_earned": reward_amount, + "current_credits": session_user.credits + } + ) + +@app.get("/create-admin") +async def create_admin( + request: Request, + response: Response, +): + """Create an admin user (only for development)""" + # Check if admin already exists + admin_exists = False + admin_user = None + + for session in SESSIONS.values(): + if session.get("user") and session["user"].username == "admin": + admin_exists = True + admin_user = session["user"] + break + + if admin_exists: + # Update admin credits + admin_user.credits = 10000 + admin_user.role = "admin" + return JSONResponse( + content={ + "success": True, + "message": "Admin user updated with 10000 credits", + "user_id": admin_user.id + } + ) + + # Create admin user + user_id = "admin" + user = UserInfo( + id=user_id, + username="admin", + email="admin@example.com", + credits=10000, + role="admin", + last_login=datetime.now().isoformat() + ) + + # In production, you would hash the password + # For simplicity, we'll store it in a way that the login function can use it + # This is for development only, never do this in production! + admin_info = { + "user": user, + "password": "admin123", # Plaintext for demo only + "created_at": datetime.now().isoformat() + } + + # Store admin info for login + API_KEYS["admin_key"] = admin_info + + return JSONResponse( + content={ + "success": True, + "message": "Admin user created with 10000 credits", + "user_id": user_id, + "username": "admin", + "password": "admin123" + } + ) + +@app.get("/admin", response_class=HTMLResponse) +async def admin_dashboard( + request: Request, + user: UserInfo = Depends(lambda: require_session_user(admin_required=True)) +): + """Admin dashboard""" + # Get all users + users = [] + for session_id, session in SESSIONS.items(): + if "user" in session: + users.append({ + "id": session["user"].id, + "username": session["user"].username, + "email": session["user"].email, + "credits": session["user"].credits, + "role": getattr(session["user"], "role", "user"), + "session_id": session_id, + "expires": session["expires"].isoformat() if "expires" in session else None + }) + + # Sort users by credits (descending) + users.sort(key=lambda x: x["credits"], reverse=True) + + return templates.TemplateResponse( + "admin_dashboard.html", + { + "request": request, + "app_name": "AI Tool Hub", + "user": user, + "user_credits": user.credits, + "users": users, + "total_users": len(users), + "total_credits": sum(u["credits"] for u in users), + "admin_count": sum(1 for u in users if u["role"] == "admin") + } + ) + +@app.post("/api/admin/update-credits") +async def admin_update_credits( + request: Request, + user: UserInfo = Depends(lambda: require_session_user(admin_required=True)) +): + """Admin endpoint to update user credits""" + data = await request.json() + user_id = data.get("user_id") + new_credits = data.get("credits") + + if not user_id or new_credits is None: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"success": False, "error": "User ID and credits are required"} + ) + + # Find the user + target_user = None + for session in SESSIONS.values(): + if "user" in session and session["user"].id == user_id: + target_user = session["user"] + break + + if not target_user: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={"success": False, "error": "User not found"} + ) + + # Update credits + try: + target_user.credits = float(new_credits) + except ValueError: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content={"success": False, "error": "Invalid credit amount"} + ) + + return JSONResponse( + content={ + "success": True, + "user_id": user_id, + "new_credits": target_user.credits, + "message": f"Credits updated for user {target_user.username}" + } + ) + +@app.post("/process-request", response_class=HTMLResponse) +async def process_request( + request: Request, + tool_id: str = Form(...), + prompt: str = Form(...), + provider: str = Form("openai"), + model: str = Form("default"), + session_user: Optional[UserInfo] = Depends(get_session_user) +): + """Process a tool request from a form""" + # Get the tool + tool = next((t for t in TOOLS if t.id == tool_id), None) + + if not tool: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Tool Not Found", + "error_description": "The requested tool does not exist.", + "user": session_user, + "user_credits": session_user.credits if session_user else 0, + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + + # Confirm that user is logged in + if not session_user: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Login Required", + "error_description": "You must be logged in to use AI tools.", + "user": None, + "user_credits": 0, + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + + # Check if the user has enough credits + if session_user.credits < tool.cost: + return templates.TemplateResponse( + "ad_reward.html", + { + "request": request, + "app_name": "AI Tool Hub", + "user": session_user, + "user_credits": session_user.credits, + "tool": tool, + "reward_ad": ads_manager.get_ad_code("reward_video"), + "sidebar_ad": ads_manager.get_ad_code("sidebar"), + "return_url": f"/tool/{tool_id}", + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + + # Get the provider + try: + provider_instance = get_provider(provider) + if not provider_instance: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Provider Not Supported", + "error_description": f"The provider '{provider}' is not supported.", + "user": session_user, + "user_credits": session_user.credits if session_user else 0, + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + + result = None + result_type = "text" # Default type + + # Determine result type based on tool ID + if "image" in tool.id or "logo" in tool.id or "avatar" in tool.id: + result_type = "image" + elif "code" in tool.id or "debugging" in tool.id: + result_type = "code" + + # Process the request based on tool type + try: + if result_type == "text": + # Text generation tool + result = provider_instance.generate_text( + prompt=prompt, + model=model if model != "default" else "mistralai/Mistral-7B-Instruct-v0.2", + max_tokens=1000, + temperature=0.7 + ) + elif result_type == "image": + # Image generation tool + try: + result = provider_instance.generate_image( + prompt=prompt, + model=model if model != "default" else "stabilityai/stable-diffusion-xl-base-1.0" + ) + except AttributeError: + # Provider doesn't support image generation + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Provider Not Supported", + "error_description": f"The provider '{provider}' does not support image generation.", + "user": session_user, + "user_credits": session_user.credits if session_user else 0, + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + elif result_type == "code": + # Code generation tool + result = provider_instance.generate_text( + prompt=prompt, + model=model if model != "default" else "gpt2", + max_tokens=1500, + temperature=0.5 + ) + else: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Tool Type Not Supported", + "error_description": f"The tool type '{result_type}' is not supported.", + "user": session_user, + "user_credits": session_user.credits if session_user else 0, + "tools": TOOLS # Include for consistent sidebar navigation + } + ) + + # Deduct credits + session_user.credits -= tool.cost + + # Return the result + return templates.TemplateResponse( + "result.html", + { + "request": request, + "app_name": "AI Tool Hub", + "user": session_user, + "user_credits": session_user.credits, + "tool": tool, + "prompt": prompt, + "result": result, + "result_type": result_type, + "tools": TOOLS, # Include all tools for sidebar navigation + "json_data": result.get("raw_response", {}) if isinstance(result, dict) else {} + } + ) + except Exception as generate_error: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Generation Error", + "error_description": f"Error generating content: {str(generate_error)}", + "user": session_user, + "user_credits": session_user.credits, + "tools": TOOLS # Include all tools for sidebar navigation + } + ) + except Exception as provider_error: + return templates.TemplateResponse( + "error.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error_title": "Provider Error", + "error_description": f"Error with provider: {str(provider_error)}", + "user": session_user, + "user_credits": session_user.credits, + "tools": TOOLS # Include all tools for sidebar navigation + } + ) + +@app.post("/register") +async def register( + request: Request, + response: Response, + username: str = Form(...), + email: str = Form(...), + password: str = Form(...), + session_id: Optional[str] = Cookie(None) +): + """Process registration""" + # Check if username or email already exists (in production use a database) + for session in SESSIONS.values(): + if session.get("user"): + if session["user"].username.lower() == username.lower(): + return templates.TemplateResponse( + "register.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error": "Username already exists", + "form_data": {"username": username, "email": email} + }, + status_code=400 + ) + if session["user"].email.lower() == email.lower(): + return templates.TemplateResponse( + "register.html", + { + "request": request, + "app_name": "AI Tool Hub", + "error": "Email already exists", + "form_data": {"username": username, "email": email} + }, + status_code=400 + ) + + # Create user + user_id = f"user_{username.lower().replace(' ', '_')}" + + # Check if there's an existing temporary session to convert + is_converting_temp = False + temp_credits = 0 + + if session_id and session_id in SESSIONS: + session = SESSIONS[session_id] + if session.get("is_temporary", False): + # Convert temporary session to permanent + is_converting_temp = True + temp_credits = session["user"].credits + # Delete the temporary session + del SESSIONS[session_id] + + # Create new user with starting credits (more if converting from temp) + start_credits = 10.0 + temp_credits + user = UserInfo( + id=user_id, + username=username, + email=email, + credits=start_credits, + last_login=datetime.now().isoformat() + ) + + # In production, you would hash the password and store in database + + # Create new session + new_session_id = secrets.token_urlsafe(32) + SESSIONS[new_session_id] = { + "user": user, + "expires": datetime.now() + timedelta(hours=24) + } + + # Set session cookie + response = RedirectResponse(url="/", status_code=status.HTTP_303_SEE_OTHER) + response.set_cookie( + key="session_id", + value=new_session_id, + max_age=86400, # 24 hours + httponly=True, + samesite="lax" + ) + + return response + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8006) \ No newline at end of file diff --git a/magicAi.db b/magicAi.db new file mode 100644 index 0000000000000000000000000000000000000000..4ee516a461525149935965762d54368995284f1a Binary files /dev/null and b/magicAi.db differ diff --git a/models.py b/models.py new file mode 100644 index 0000000000000000000000000000000000000000..adf6ef65b3346aedef27b1587c8d6d7b85cc569a --- /dev/null +++ b/models.py @@ -0,0 +1,21 @@ +from sqlalchemy import Column, String, Integer, Float +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() + +class User(Base): + __tablename__ = 'users' + + id = Column(Integer, primary_key=True, index=True) + username = Column(String, unique=True, index=True) + email = Column(String, unique=True, index=True) + credits = Column(Float, default=10.0) + +class Prompt(Base): + __tablename__ = 'prompts' + + id = Column(String, primary_key=True, index=True) + title = Column(String, index=True) + content = Column(String) + description = Column(String, nullable=True) + creator_id = Column(String, index=True) diff --git a/prompts/__init__.py b/prompts/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6bd9feba7afecf86f0d61083edbbdb02e5ec0920 --- /dev/null +++ b/prompts/__init__.py @@ -0,0 +1,13 @@ +""" +Prompts Package +Exports prompt template and marketplace classes +""" + +from prompts.prompt_templates import PromptTemplate, PromptTemplateManager +from prompts.prompt_marketplace import PromptMarketplace + +__all__ = [ + 'PromptTemplate', + 'PromptTemplateManager', + 'PromptMarketplace' +] \ No newline at end of file diff --git a/prompts/__pycache__/__init__.cpython-310.pyc b/prompts/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55e8c142521a80541cc791f7c539557596f19c8c Binary files /dev/null and b/prompts/__pycache__/__init__.cpython-310.pyc differ diff --git a/prompts/__pycache__/prompt_marketplace.cpython-310.pyc b/prompts/__pycache__/prompt_marketplace.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b967f4a8cdbe552d5dd6cfa647623c94fcc8849 Binary files /dev/null and b/prompts/__pycache__/prompt_marketplace.cpython-310.pyc differ diff --git a/prompts/__pycache__/prompt_templates.cpython-310.pyc b/prompts/__pycache__/prompt_templates.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4bc101c6d4da875c0afdec9aa21a4961d4468a08 Binary files /dev/null and b/prompts/__pycache__/prompt_templates.cpython-310.pyc differ diff --git a/prompts/marketplace_data/purchases.json b/prompts/marketplace_data/purchases.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/prompts/marketplace_data/purchases.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/prompts/marketplace_data/sales.json b/prompts/marketplace_data/sales.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/prompts/marketplace_data/sales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/prompts/marketplace_data/stats.json b/prompts/marketplace_data/stats.json new file mode 100644 index 0000000000000000000000000000000000000000..4d10b46e0a22568072eec9fb8cba6180bdf8a3d5 --- /dev/null +++ b/prompts/marketplace_data/stats.json @@ -0,0 +1,6 @@ +{ + "total_sales": 0, + "total_revenue": 0, + "prompt_usage": {}, + "popular_categories": {} +} \ No newline at end of file diff --git a/prompts/prompt_marketplace.py b/prompts/prompt_marketplace.py new file mode 100644 index 0000000000000000000000000000000000000000..18ce2aa50ac0b99a75b5b044f7c28821b79deca5 --- /dev/null +++ b/prompts/prompt_marketplace.py @@ -0,0 +1,728 @@ +""" +Prompt Marketplace Module +Enables users to create, sell, purchase, and use AI prompts +""" +import os +import json +import uuid +import logging +from typing import Dict, Any, List, Optional +from datetime import datetime + +from prompts.prompt_templates import PromptTemplate, PromptTemplateManager + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("prompt_marketplace") + +class PromptMarketplace: + """Manages the prompt marketplace functionality""" + + def __init__(self, data_dir: str = None): + """Initialize the prompt marketplace""" + self.data_dir = data_dir or os.path.join(os.path.dirname(__file__), "marketplace_data") + + # Create data directory if it doesn't exist + os.makedirs(self.data_dir, exist_ok=True) + + # Paths for data files + self.purchases_file = os.path.join(self.data_dir, "purchases.json") + self.sales_file = os.path.join(self.data_dir, "sales.json") + self.stats_file = os.path.join(self.data_dir, "stats.json") + + # Initialize data files if they don't exist + self._initialize_data_files() + + # Create prompt template manager + self.template_manager = PromptTemplateManager() + + def _initialize_data_files(self): + """Initialize data files if they don't exist""" + if not os.path.exists(self.purchases_file): + with open(self.purchases_file, "w") as f: + json.dump([], f) + + if not os.path.exists(self.sales_file): + with open(self.sales_file, "w") as f: + json.dump([], f) + + if not os.path.exists(self.stats_file): + with open(self.stats_file, "w") as f: + json.dump({ + "total_sales": 0, + "total_revenue": 0, + "prompt_usage": {}, + "popular_categories": {} + }, f, indent=2) + + def list_marketplace_prompts(self, category: str = None, sort_by: str = "popular") -> List[Dict[str, Any]]: + """ + List prompts available in the marketplace + + Args: + category: Optional category to filter by + sort_by: Sorting method ('popular', 'newest', 'price_low', 'price_high') + + Returns: + List of prompt templates available for purchase + """ + # Get all public templates + all_templates = self.template_manager.get_public_templates() + + # Filter by category if specified + if category: + all_templates = [t for t in all_templates if t.category.lower() == category.lower()] + + # Convert to dictionaries for easier manipulation + templates_dict = [t.to_dict() for t in all_templates] + + # Add usage statistics + templates_with_stats = self._add_stats_to_templates(templates_dict) + + # Sort the templates + if sort_by == "popular": + templates_with_stats.sort(key=lambda x: x.get("stats", {}).get("usage_count", 0), reverse=True) + elif sort_by == "newest": + templates_with_stats.sort(key=lambda x: x.get("created_at", ""), reverse=True) + elif sort_by == "price_low": + templates_with_stats.sort(key=lambda x: x.get("price", 0)) + elif sort_by == "price_high": + templates_with_stats.sort(key=lambda x: x.get("price", 0), reverse=True) + + return templates_with_stats + + def _add_stats_to_templates(self, templates: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """Add usage statistics to template dictionaries""" + try: + with open(self.stats_file, "r") as f: + stats = json.load(f) + + prompt_usage = stats.get("prompt_usage", {}) + + # Add stats to each template + for template in templates: + template_id = template.get("id") + template["stats"] = { + "usage_count": prompt_usage.get(template_id, {}).get("count", 0), + "purchase_count": prompt_usage.get(template_id, {}).get("purchases", 0), + "rating": prompt_usage.get(template_id, {}).get("avg_rating", 0), + "reviews": prompt_usage.get(template_id, {}).get("review_count", 0) + } + + return templates + except Exception as e: + logger.error(f"Error adding stats to templates: {e}") + return templates + + def get_prompt_details(self, prompt_id: str) -> Dict[str, Any]: + """ + Get detailed information about a marketplace prompt + + Args: + prompt_id: ID of the prompt template + + Returns: + Detailed prompt information including stats and sample outputs + """ + prompt = self.template_manager.get_template(prompt_id) + + if not prompt: + return {"success": False, "error": "Prompt not found"} + + if not prompt.is_public and prompt.created_by != "system": + return {"success": False, "error": "Prompt is not available in the marketplace"} + + # Get prompt details + prompt_dict = prompt.to_dict() + + # Add stats + prompt_dict = self._add_stats_to_templates([prompt_dict])[0] + + # Get purchase information + prompt_dict["purchases"] = self._get_prompt_purchases(prompt_id) + + return { + "success": True, + "prompt": prompt_dict + } + + def _get_prompt_purchases(self, prompt_id: str) -> Dict[str, Any]: + """Get purchase information for a prompt""" + try: + with open(self.sales_file, "r") as f: + sales = json.load(f) + + # Filter sales for this prompt + prompt_sales = [s for s in sales if s.get("prompt_id") == prompt_id] + + return { + "total": len(prompt_sales), + "recent": len([s for s in prompt_sales if + (datetime.now() - datetime.fromisoformat(s.get("timestamp", ""))).days < 7]) + } + except Exception as e: + logger.error(f"Error getting prompt purchases: {e}") + return {"total": 0, "recent": 0} + + def create_prompt_for_sale(self, + name: str, + description: str, + template: str, + system_message: str, + category: str, + price: float, + parameters: Dict[str, Any], + created_by: str, + provider_defaults: Dict[str, Any] = None) -> Dict[str, Any]: + """ + Create a new prompt template for sale in the marketplace + + Args: + name: Name of the prompt template + description: Description of what the prompt does + template: The prompt template text + system_message: System message for the prompt + category: Category for the prompt + price: Price in credits + parameters: Dictionary of parameters for the prompt + created_by: User ID of the creator + provider_defaults: Default settings for different providers + + Returns: + Result dictionary with success status and prompt information + """ + # Validate inputs + if not name or not template: + return {"success": False, "error": "Name and template are required"} + + if price < 0: + return {"success": False, "error": "Price cannot be negative"} + + # Create the prompt template + new_prompt = PromptTemplate( + name=name, + description=description, + template=template, + system_message=system_message, + category=category, + is_public=True, # It's for sale, so make it public + created_by=created_by, + price=price, + parameters=parameters, + provider_defaults=provider_defaults or {} + ) + + # Save the prompt + created_prompt = self.template_manager.create_template(new_prompt) + + # Initialize stats for this prompt + self._initialize_prompt_stats(created_prompt.id) + + return { + "success": True, + "prompt": created_prompt.to_dict(), + "message": "Prompt successfully created and listed in the marketplace" + } + + def _initialize_prompt_stats(self, prompt_id: str): + """Initialize statistics for a new prompt""" + try: + with open(self.stats_file, "r") as f: + stats = json.load(f) + + prompt_usage = stats.get("prompt_usage", {}) + prompt_usage[prompt_id] = { + "count": 0, + "purchases": 0, + "avg_rating": 0, + "review_count": 0, + "last_used": None + } + + stats["prompt_usage"] = prompt_usage + + with open(self.stats_file, "w") as f: + json.dump(stats, f, indent=2) + + except Exception as e: + logger.error(f"Error initializing prompt stats: {e}") + + def purchase_prompt(self, prompt_id: str, user_id: str, credits_available: float) -> Dict[str, Any]: + """ + Purchase a prompt from the marketplace + + Args: + prompt_id: ID of the prompt to purchase + user_id: ID of the user making the purchase + credits_available: Credits available to the user + + Returns: + Result dictionary with success status and transaction details + """ + # Get the prompt + prompt = self.template_manager.get_template(prompt_id) + + if not prompt: + return {"success": False, "error": "Prompt not found"} + + if not prompt.is_public: + return {"success": False, "error": "Prompt is not available for purchase"} + + # Check if user already owns this prompt + if self._user_owns_prompt(user_id, prompt_id): + return {"success": False, "error": "You already own this prompt"} + + # Check if user has enough credits + if credits_available < prompt.price: + return {"success": False, "error": "Insufficient credits", "credits_needed": prompt.price} + + # Process the purchase + purchase_id = str(uuid.uuid4()) + purchase_time = datetime.now().isoformat() + + purchase_record = { + "id": purchase_id, + "prompt_id": prompt_id, + "user_id": user_id, + "seller_id": prompt.created_by, + "price": prompt.price, + "timestamp": purchase_time + } + + # Record the purchase + self._record_purchase(purchase_record) + + # Record the sale + self._record_sale(purchase_record) + + # Update statistics + self._update_stats_after_purchase(prompt_id) + + return { + "success": True, + "transaction": { + "id": purchase_id, + "prompt_id": prompt_id, + "prompt_name": prompt.name, + "price": prompt.price, + "timestamp": purchase_time + }, + "credits_used": prompt.price, + "message": f"Successfully purchased prompt: {prompt.name}" + } + + def _user_owns_prompt(self, user_id: str, prompt_id: str) -> bool: + """Check if a user already owns a prompt""" + try: + with open(self.purchases_file, "r") as f: + purchases = json.load(f) + + # Check if the user has already purchased this prompt + for purchase in purchases: + if purchase.get("user_id") == user_id and purchase.get("prompt_id") == prompt_id: + return True + + return False + + except Exception as e: + logger.error(f"Error checking if user owns prompt: {e}") + return False + + def _record_purchase(self, purchase_record: Dict[str, Any]): + """Record a prompt purchase""" + try: + with open(self.purchases_file, "r") as f: + purchases = json.load(f) + + purchases.append(purchase_record) + + with open(self.purchases_file, "w") as f: + json.dump(purchases, f, indent=2) + + except Exception as e: + logger.error(f"Error recording purchase: {e}") + + def _record_sale(self, purchase_record: Dict[str, Any]): + """Record a prompt sale""" + try: + with open(self.sales_file, "r") as f: + sales = json.load(f) + + sales.append(purchase_record) + + with open(self.sales_file, "w") as f: + json.dump(sales, f, indent=2) + + except Exception as e: + logger.error(f"Error recording sale: {e}") + + def _update_stats_after_purchase(self, prompt_id: str): + """Update statistics after a prompt purchase""" + try: + with open(self.stats_file, "r") as f: + stats = json.load(f) + + # Update prompt-specific stats + prompt_usage = stats.get("prompt_usage", {}) + if prompt_id not in prompt_usage: + prompt_usage[prompt_id] = { + "count": 0, + "purchases": 0, + "avg_rating": 0, + "review_count": 0 + } + + prompt_usage[prompt_id]["purchases"] = prompt_usage[prompt_id].get("purchases", 0) + 1 + + # Update global stats + stats["total_sales"] = stats.get("total_sales", 0) + 1 + + # Get the prompt price + prompt = self.template_manager.get_template(prompt_id) + if prompt: + stats["total_revenue"] = stats.get("total_revenue", 0) + prompt.price + + # Update category popularity + category = prompt.category + popular_categories = stats.get("popular_categories", {}) + popular_categories[category] = popular_categories.get(category, 0) + 1 + stats["popular_categories"] = popular_categories + + # Save updated stats + with open(self.stats_file, "w") as f: + json.dump(stats, f, indent=2) + + except Exception as e: + logger.error(f"Error updating stats after purchase: {e}") + + def get_user_purchased_prompts(self, user_id: str) -> List[Dict[str, Any]]: + """ + Get prompts purchased by a specific user + + Args: + user_id: ID of the user + + Returns: + List of prompts owned by the user + """ + purchased_prompt_ids = self._get_user_purchased_prompt_ids(user_id) + + # Get the prompt details for each purchased prompt + purchased_prompts = [] + for prompt_id in purchased_prompt_ids: + prompt = self.template_manager.get_template(prompt_id) + if prompt: + prompt_dict = prompt.to_dict() + purchased_prompts.append(prompt_dict) + + return purchased_prompts + + def _get_user_purchased_prompt_ids(self, user_id: str) -> List[str]: + """Get IDs of prompts purchased by a user""" + try: + with open(self.purchases_file, "r") as f: + purchases = json.load(f) + + # Get unique prompt IDs purchased by this user + prompt_ids = set() + for purchase in purchases: + if purchase.get("user_id") == user_id: + prompt_ids.add(purchase.get("prompt_id")) + + return list(prompt_ids) + + except Exception as e: + logger.error(f"Error getting user purchased prompt IDs: {e}") + return [] + + def get_user_sales(self, user_id: str) -> Dict[str, Any]: + """ + Get sales information for a specific seller + + Args: + user_id: ID of the seller + + Returns: + Dictionary with sales information + """ + try: + with open(self.sales_file, "r") as f: + sales = json.load(f) + + # Filter sales by this seller + user_sales = [s for s in sales if s.get("seller_id") == user_id] + + # Calculate total revenue + total_revenue = sum(s.get("price", 0) for s in user_sales) + + # Group sales by prompt + sales_by_prompt = {} + for sale in user_sales: + prompt_id = sale.get("prompt_id") + if prompt_id not in sales_by_prompt: + sales_by_prompt[prompt_id] = [] + sales_by_prompt[prompt_id].append(sale) + + # Get prompt details and calculate stats for each prompt + prompt_sales = [] + for prompt_id, sales_list in sales_by_prompt.items(): + prompt = self.template_manager.get_template(prompt_id) + if not prompt: + continue + + prompt_revenue = sum(s.get("price", 0) for s in sales_list) + + prompt_sales.append({ + "prompt_id": prompt_id, + "prompt_name": prompt.name, + "price": prompt.price, + "sales_count": len(sales_list), + "revenue": prompt_revenue, + "last_sale": max(s.get("timestamp", "") for s in sales_list) + }) + + # Sort by revenue + prompt_sales.sort(key=lambda x: x.get("revenue", 0), reverse=True) + + return { + "success": True, + "total_sales": len(user_sales), + "total_revenue": total_revenue, + "prompt_count": len(prompt_sales), + "prompts": prompt_sales + } + + except Exception as e: + logger.error(f"Error getting user sales: {e}") + return { + "success": False, + "error": str(e) + } + + def record_prompt_usage(self, prompt_id: str, user_id: str, provider: str = None) -> bool: + """ + Record usage of a prompt + + Args: + prompt_id: ID of the prompt used + user_id: ID of the user using the prompt + provider: Optional provider used + + Returns: + Success status + """ + try: + with open(self.stats_file, "r") as f: + stats = json.load(f) + + # Update prompt usage stats + prompt_usage = stats.get("prompt_usage", {}) + if prompt_id not in prompt_usage: + prompt_usage[prompt_id] = { + "count": 0, + "purchases": 0, + "avg_rating": 0, + "review_count": 0 + } + + prompt_usage[prompt_id]["count"] = prompt_usage[prompt_id].get("count", 0) + 1 + prompt_usage[prompt_id]["last_used"] = datetime.now().isoformat() + + # Track provider usage if provided + if provider: + providers = prompt_usage[prompt_id].get("providers", {}) + providers[provider] = providers.get(provider, 0) + 1 + prompt_usage[prompt_id]["providers"] = providers + + # Save updated stats + with open(self.stats_file, "w") as f: + json.dump(stats, f, indent=2) + + return True + + except Exception as e: + logger.error(f"Error recording prompt usage: {e}") + return False + + def rate_prompt(self, prompt_id: str, user_id: str, rating: int, review: str = None) -> Dict[str, Any]: + """ + Rate and review a prompt + + Args: + prompt_id: ID of the prompt to rate + user_id: ID of the user providing the rating + rating: Rating value (1-5) + review: Optional review text + + Returns: + Result dictionary with success status + """ + # Validate rating + if rating < 1 or rating > 5: + return {"success": False, "error": "Rating must be between 1 and 5"} + + # Check if the user owns the prompt + if not self._user_owns_prompt(user_id, prompt_id): + return {"success": False, "error": "You must purchase a prompt before rating it"} + + try: + # Update the stats with the new rating + with open(self.stats_file, "r") as f: + stats = json.load(f) + + prompt_usage = stats.get("prompt_usage", {}) + if prompt_id not in prompt_usage: + return {"success": False, "error": "Prompt not found"} + + # Calculate new average rating + current_avg = prompt_usage[prompt_id].get("avg_rating", 0) + current_count = prompt_usage[prompt_id].get("review_count", 0) + + if current_count == 0: + new_avg = rating + else: + new_avg = (current_avg * current_count + rating) / (current_count + 1) + + prompt_usage[prompt_id]["avg_rating"] = new_avg + prompt_usage[prompt_id]["review_count"] = current_count + 1 + + # Save the review if provided + if review: + reviews = prompt_usage[prompt_id].get("reviews", []) + reviews.append({ + "user_id": user_id, + "rating": rating, + "review": review, + "timestamp": datetime.now().isoformat() + }) + prompt_usage[prompt_id]["reviews"] = reviews + + # Save updated stats + with open(self.stats_file, "w") as f: + json.dump(stats, f, indent=2) + + return { + "success": True, + "message": "Rating submitted successfully", + "new_rating": new_avg, + "review_count": current_count + 1 + } + + except Exception as e: + logger.error(f"Error rating prompt: {e}") + return { + "success": False, + "error": str(e) + } + + def get_marketplace_stats(self) -> Dict[str, Any]: + """Get overall marketplace statistics""" + try: + with open(self.stats_file, "r") as f: + stats = json.load(f) + + # Get the top prompts by usage + prompt_usage = stats.get("prompt_usage", {}) + top_prompts = [] + + for prompt_id, usage in prompt_usage.items(): + prompt = self.template_manager.get_template(prompt_id) + if not prompt: + continue + + top_prompts.append({ + "id": prompt_id, + "name": prompt.name, + "creator": prompt.created_by, + "category": prompt.category, + "price": prompt.price, + "usage_count": usage.get("count", 0), + "purchase_count": usage.get("purchases", 0), + "rating": usage.get("avg_rating", 0), + "review_count": usage.get("review_count", 0) + }) + + # Sort by usage count + top_prompts.sort(key=lambda x: x.get("usage_count", 0), reverse=True) + top_prompts = top_prompts[:10] # Get top 10 + + # Get top categories + categories = stats.get("popular_categories", {}) + top_categories = [{"category": k, "count": v} for k, v in categories.items()] + top_categories.sort(key=lambda x: x.get("count", 0), reverse=True) + + return { + "success": True, + "total_sales": stats.get("total_sales", 0), + "total_revenue": stats.get("total_revenue", 0), + "top_prompts": top_prompts, + "top_categories": top_categories + } + + except Exception as e: + logger.error(f"Error getting marketplace stats: {e}") + return { + "success": False, + "error": str(e) + } + +# Example usage +if __name__ == "__main__": + # Initialize the marketplace + marketplace = PromptMarketplace() + + # Create a sample prompt for sale + prompt_result = marketplace.create_prompt_for_sale( + name="Advanced SEO Article Writer", + description="Generate comprehensive SEO-optimized articles with proper keyword placement and structure", + template="Write a {length} word SEO-optimized article about {topic}. Target the keyword {keyword} with a keyword density of {density}%. Include {headings} headings, a compelling introduction, and a conclusion with call-to-action.", + system_message="You are an expert SEO content writer who creates engaging, well-researched content that ranks well in search engines.", + category="marketing", + price=25.0, + parameters={ + "topic": {"type": "string", "description": "Main topic of the article", "required": True}, + "keyword": {"type": "string", "description": "Target keyword to optimize for", "required": True}, + "length": {"type": "number", "description": "Word count", "default": 1500}, + "density": {"type": "number", "description": "Keyword density percentage", "default": 2}, + "headings": {"type": "number", "description": "Number of headings to include", "default": 5} + }, + created_by="seller123", + provider_defaults={ + "openai": {"model": "gpt-4-turbo"} + } + ) + + print(f"Created prompt: {prompt_result['success']}") + + if prompt_result['success']: + # Simulate a purchase + purchase_result = marketplace.purchase_prompt( + prompt_id=prompt_result['prompt']['id'], + user_id="buyer456", + credits_available=100.0 + ) + + print(f"Purchase result: {purchase_result['success']}") + + # Record usage of the prompt + marketplace.record_prompt_usage( + prompt_id=prompt_result['prompt']['id'], + user_id="buyer456", + provider="openai" + ) + + # Rate the prompt + rating_result = marketplace.rate_prompt( + prompt_id=prompt_result['prompt']['id'], + user_id="buyer456", + rating=5, + review="This prompt generated an excellent SEO article that ranked quickly!" + ) + + print(f"Rating result: {rating_result['success']}") + + # Get marketplace stats + stats = marketplace.get_marketplace_stats() + print(f"Marketplace stats: {stats['success']}") + if stats['success']: + print(f"Total sales: {stats['total_sales']}") \ No newline at end of file diff --git a/prompts/prompt_templates.py b/prompts/prompt_templates.py new file mode 100644 index 0000000000000000000000000000000000000000..582842248b5470339ea2aaaa5f1113ee5045c095 --- /dev/null +++ b/prompts/prompt_templates.py @@ -0,0 +1,350 @@ +""" +Prompt Templates Module +Manages prompt templates for different AI tools and providers +Includes system for storing, loading, and managing user-created prompts +""" +import os +import json +import uuid +import logging +from typing import Dict, Any, List, Optional +from datetime import datetime + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("prompts") + +class PromptTemplate: + """Represents a prompt template that can be used with AI providers""" + + def __init__(self, + id: str = None, + name: str = "", + description: str = "", + template: str = "", + system_message: str = "", + category: str = "", + is_public: bool = False, + created_by: str = "system", + created_at: str = None, + price: float = 0.0, + parameters: Dict[str, Any] = None, + provider_defaults: Dict[str, Any] = None): + """Initialize a prompt template""" + self.id = id or str(uuid.uuid4()) + self.name = name + self.description = description + self.template = template + self.system_message = system_message + self.category = category + self.is_public = is_public + self.created_by = created_by + self.created_at = created_at or datetime.now().isoformat() + self.price = price + self.parameters = parameters or {} + self.provider_defaults = provider_defaults or {} + + def to_dict(self) -> Dict[str, Any]: + """Convert template to dictionary""" + return { + "id": self.id, + "name": self.name, + "description": self.description, + "template": self.template, + "system_message": self.system_message, + "category": self.category, + "is_public": self.is_public, + "created_by": self.created_by, + "created_at": self.created_at, + "price": self.price, + "parameters": self.parameters, + "provider_defaults": self.provider_defaults + } + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> 'PromptTemplate': + """Create template from dictionary""" + return cls( + id=data.get("id"), + name=data.get("name", ""), + description=data.get("description", ""), + template=data.get("template", ""), + system_message=data.get("system_message", ""), + category=data.get("category", ""), + is_public=data.get("is_public", False), + created_by=data.get("created_by", "system"), + created_at=data.get("created_at"), + price=data.get("price", 0.0), + parameters=data.get("parameters", {}), + provider_defaults=data.get("provider_defaults", {}) + ) + + def render(self, variables: Dict[str, Any] = None) -> Dict[str, str]: + """ + Render the prompt template with the provided variables + Returns both the rendered prompt and system message + """ + variables = variables or {} + prompt = self.template + system = self.system_message + + # Replace variables in the template + for key, value in variables.items(): + placeholder = "{" + key + "}" + prompt = prompt.replace(placeholder, str(value)) + system = system.replace(placeholder, str(value)) + + return { + "prompt": prompt, + "system_message": system + } + +class PromptTemplateManager: + """Manages prompt templates, including default and user-created ones""" + + def __init__(self, templates_dir: str = None): + """Initialize the prompt template manager""" + self.templates_dir = templates_dir or os.path.join(os.path.dirname(__file__), "templates") + self.user_templates_dir = os.path.join(self.templates_dir, "user") + + # Create directories if they don't exist + os.makedirs(self.templates_dir, exist_ok=True) + os.makedirs(self.user_templates_dir, exist_ok=True) + + # Load default templates + self.default_templates = self._load_default_templates() + + # Load user templates + self.user_templates = self._load_user_templates() + + def _load_default_templates(self) -> Dict[str, PromptTemplate]: + """Load default templates from files""" + templates = {} + + # Define default templates if no files exist yet + default_templates = { + "general_chat": PromptTemplate( + id="general_chat", + name="General Chat", + description="A general-purpose chat prompt", + template="Please answer the following question or respond to the message: {input}", + system_message="You are a helpful assistant that provides accurate and concise responses.", + category="general", + created_by="system" + ), + "creative_writing": PromptTemplate( + id="creative_writing", + name="Creative Writing", + description="Generate creative writing based on a premise", + template="Write a {genre} {format} about {topic}.", + system_message="You are a creative writer with expertise in different genres and formats.", + category="creative", + created_by="system", + parameters={ + "genre": {"type": "string", "description": "Genre of the writing", "default": "science fiction"}, + "format": {"type": "string", "description": "Format of the writing", "default": "short story"}, + "topic": {"type": "string", "description": "Topic or premise", "required": True} + } + ), + "code_assistant": PromptTemplate( + id="code_assistant", + name="Code Assistant", + description="Generate or debug code in various languages", + template="I need help with the following code task in {language}:\n\n{task}\n\n{code}", + system_message="You are an expert programmer. Provide well-commented, efficient, and correct code solutions.", + category="development", + created_by="system", + parameters={ + "language": {"type": "string", "description": "Programming language", "required": True}, + "task": {"type": "string", "description": "Description of the coding task", "required": True}, + "code": {"type": "string", "description": "Existing code (if any)", "default": ""} + }, + provider_defaults={ + "openai": {"model": "gpt-4-turbo"}, + "deepseek": {"model": "deepseek-coder"} + } + ), + "image_prompt": PromptTemplate( + id="image_prompt", + name="Image Generation", + description="Detailed prompt for image generation", + template="{subject} {style}, {details}, {quality}", + system_message="", + category="images", + created_by="system", + parameters={ + "subject": {"type": "string", "description": "Main subject of the image", "required": True}, + "style": {"type": "string", "description": "Art style", "default": "digital art"}, + "details": {"type": "string", "description": "Additional details", "default": "detailed, vibrant colors"}, + "quality": {"type": "string", "description": "Quality descriptors", "default": "high quality, 4k, trending on artstation"} + }, + provider_defaults={ + "openai": {"model": "dall-e-3"} + } + ) + } + + # Save default templates if they don't exist + for template_id, template in default_templates.items(): + template_path = os.path.join(self.templates_dir, f"{template_id}.json") + + if not os.path.exists(template_path): + with open(template_path, "w") as f: + json.dump(template.to_dict(), f, indent=2) + + templates[template_id] = template + + return templates + + def _load_user_templates(self) -> Dict[str, PromptTemplate]: + """Load user-created templates""" + templates = {} + + try: + # List all JSON files in user_templates_dir + for filename in os.listdir(self.user_templates_dir): + if filename.endswith(".json"): + template_path = os.path.join(self.user_templates_dir, filename) + + with open(template_path, "r") as f: + template_data = json.load(f) + template = PromptTemplate.from_dict(template_data) + templates[template.id] = template + except Exception as e: + logger.error(f"Error loading user templates: {e}") + + return templates + + def get_all_templates(self) -> List[PromptTemplate]: + """Get all available templates (default + user)""" + all_templates = list(self.default_templates.values()) + + # Add public user templates and user's own templates + for template in self.user_templates.values(): + if template.is_public: + all_templates.append(template) + + return all_templates + + def get_user_templates(self, user_id: str) -> List[PromptTemplate]: + """Get templates created by a specific user""" + return [t for t in self.user_templates.values() if t.created_by == user_id] + + def get_template(self, template_id: str) -> Optional[PromptTemplate]: + """Get a specific template by ID""" + if template_id in self.default_templates: + return self.default_templates[template_id] + + if template_id in self.user_templates: + return self.user_templates[template_id] + + return None + + def create_template(self, template: PromptTemplate) -> PromptTemplate: + """Create a new user template""" + # Ensure unique ID + if template.id in self.default_templates or template.id in self.user_templates: + template.id = str(uuid.uuid4()) + + # Save template to file + template_path = os.path.join(self.user_templates_dir, f"{template.id}.json") + + with open(template_path, "w") as f: + json.dump(template.to_dict(), f, indent=2) + + # Add to user templates + self.user_templates[template.id] = template + + return template + + def update_template(self, template: PromptTemplate) -> Optional[PromptTemplate]: + """Update an existing user template""" + if template.id not in self.user_templates: + logger.error(f"Template {template.id} not found or not a user template") + return None + + # Save updated template + template_path = os.path.join(self.user_templates_dir, f"{template.id}.json") + + with open(template_path, "w") as f: + json.dump(template.to_dict(), f, indent=2) + + # Update in memory + self.user_templates[template.id] = template + + return template + + def delete_template(self, template_id: str, user_id: str) -> bool: + """Delete a user template""" + if template_id not in self.user_templates: + logger.error(f"Template {template_id} not found or not a user template") + return False + + # Check ownership + template = self.user_templates[template_id] + if template.created_by != user_id and user_id != "admin": + logger.error(f"User {user_id} does not own template {template_id}") + return False + + # Delete template file + template_path = os.path.join(self.user_templates_dir, f"{template_id}.json") + + try: + os.remove(template_path) + del self.user_templates[template_id] + return True + except Exception as e: + logger.error(f"Error deleting template {template_id}: {e}") + return False + + def get_templates_by_category(self, category: str) -> List[PromptTemplate]: + """Get templates by category""" + all_templates = self.get_all_templates() + return [t for t in all_templates if t.category.lower() == category.lower()] + + def get_public_templates(self) -> List[PromptTemplate]: + """Get all public templates created by users""" + return [t for t in self.user_templates.values() if t.is_public] + +# Example usage +if __name__ == "__main__": + # Initialize manager + manager = PromptTemplateManager() + + # Get all templates + all_templates = manager.get_all_templates() + print(f"Loaded {len(all_templates)} templates") + + # Create a new template + new_template = PromptTemplate( + name="SEO Content", + description="Generate SEO-optimized content for websites", + template="Write SEO-optimized content about {topic} targeting the keyword {keyword}. {tone} tone, {length} words.", + system_message="You are an expert SEO content writer who creates engaging, well-researched content that ranks well in search engines.", + category="marketing", + created_by="user123", + is_public=True, + parameters={ + "topic": {"type": "string", "description": "Main topic", "required": True}, + "keyword": {"type": "string", "description": "Target keyword", "required": True}, + "tone": {"type": "string", "description": "Content tone", "default": "professional"}, + "length": {"type": "number", "description": "Content length in words", "default": 500} + } + ) + + created = manager.create_template(new_template) + print(f"Created new template: {created.id} - {created.name}") + + # Test rendering a template + code_template = manager.get_template("code_assistant") + if code_template: + rendered = code_template.render({ + "language": "Python", + "task": "Create a function to calculate Fibonacci numbers", + "code": "def fibonacci(n):\n # TODO: Implement" + }) + + print("\nRendered prompt:") + print(rendered["prompt"]) + print("\nSystem message:") + \ No newline at end of file diff --git a/prompts/templates/code_assistant.json b/prompts/templates/code_assistant.json new file mode 100644 index 0000000000000000000000000000000000000000..cf82c416e395797848724807d11de19d1a621937 --- /dev/null +++ b/prompts/templates/code_assistant.json @@ -0,0 +1,37 @@ +{ + "id": "code_assistant", + "name": "Code Assistant", + "description": "Generate or debug code in various languages", + "template": "I need help with the following code task in {language}:\n\n{task}\n\n{code}", + "system_message": "You are an expert programmer. Provide well-commented, efficient, and correct code solutions.", + "category": "development", + "is_public": false, + "created_by": "system", + "created_at": "2025-03-18T14:56:55.479511", + "price": 0.0, + "parameters": { + "language": { + "type": "string", + "description": "Programming language", + "required": true + }, + "task": { + "type": "string", + "description": "Description of the coding task", + "required": true + }, + "code": { + "type": "string", + "description": "Existing code (if any)", + "default": "" + } + }, + "provider_defaults": { + "openai": { + "model": "gpt-4-turbo" + }, + "deepseek": { + "model": "deepseek-coder" + } + } +} \ No newline at end of file diff --git a/prompts/templates/creative_writing.json b/prompts/templates/creative_writing.json new file mode 100644 index 0000000000000000000000000000000000000000..99331ce78df5e44a84f4eb963be6215fc3f7da9b --- /dev/null +++ b/prompts/templates/creative_writing.json @@ -0,0 +1,30 @@ +{ + "id": "creative_writing", + "name": "Creative Writing", + "description": "Generate creative writing based on a premise", + "template": "Write a {genre} {format} about {topic}.", + "system_message": "You are a creative writer with expertise in different genres and formats.", + "category": "creative", + "is_public": false, + "created_by": "system", + "created_at": "2025-03-18T14:56:55.479511", + "price": 0.0, + "parameters": { + "genre": { + "type": "string", + "description": "Genre of the writing", + "default": "science fiction" + }, + "format": { + "type": "string", + "description": "Format of the writing", + "default": "short story" + }, + "topic": { + "type": "string", + "description": "Topic or premise", + "required": true + } + }, + "provider_defaults": {} +} \ No newline at end of file diff --git a/prompts/templates/general_chat.json b/prompts/templates/general_chat.json new file mode 100644 index 0000000000000000000000000000000000000000..49b4b6bd65e4edb16580715932214509c89154ee --- /dev/null +++ b/prompts/templates/general_chat.json @@ -0,0 +1,14 @@ +{ + "id": "general_chat", + "name": "General Chat", + "description": "A general-purpose chat prompt", + "template": "Please answer the following question or respond to the message: {input}", + "system_message": "You are a helpful assistant that provides accurate and concise responses.", + "category": "general", + "is_public": false, + "created_by": "system", + "created_at": "2025-03-18T14:56:55.479511", + "price": 0.0, + "parameters": {}, + "provider_defaults": {} +} \ No newline at end of file diff --git a/prompts/templates/image_prompt.json b/prompts/templates/image_prompt.json new file mode 100644 index 0000000000000000000000000000000000000000..b48f60c7f6b65017ec91caf432402d9105bd99e6 --- /dev/null +++ b/prompts/templates/image_prompt.json @@ -0,0 +1,39 @@ +{ + "id": "image_prompt", + "name": "Image Generation", + "description": "Detailed prompt for image generation", + "template": "{subject} {style}, {details}, {quality}", + "system_message": "", + "category": "images", + "is_public": false, + "created_by": "system", + "created_at": "2025-03-18T14:56:55.479511", + "price": 0.0, + "parameters": { + "subject": { + "type": "string", + "description": "Main subject of the image", + "required": true + }, + "style": { + "type": "string", + "description": "Art style", + "default": "digital art" + }, + "details": { + "type": "string", + "description": "Additional details", + "default": "detailed, vibrant colors" + }, + "quality": { + "type": "string", + "description": "Quality descriptors", + "default": "high quality, 4k, trending on artstation" + } + }, + "provider_defaults": { + "openai": { + "model": "dall-e-3" + } + } +} \ No newline at end of file diff --git a/providers/__init__.py b/providers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d74437427e192794aab30d86be7791e29ff4b02d --- /dev/null +++ b/providers/__init__.py @@ -0,0 +1,41 @@ +""" +AI Providers Package +Exports provider classes for different AI model providers +""" + +from providers.huggingface import HuggingFaceProvider +from providers.openai import OpenAIProvider +from providers.deepseek import DeepSeekProvider +from providers.openrouter import OpenRouterProvider + +__all__ = [ + 'HuggingFaceProvider', + 'OpenAIProvider', + 'DeepSeekProvider', + 'OpenRouterProvider' +] + +# Provider registry for easy access +PROVIDERS = { + 'huggingface': HuggingFaceProvider, + 'openai': OpenAIProvider, + 'deepseek': DeepSeekProvider, + 'openrouter': OpenRouterProvider +} + +def get_provider(provider_name: str, api_key: str = None): + """ + Get a provider instance by name + + Args: + provider_name: Name of the provider ('huggingface', 'openai', etc.) + api_key: Optional API key to use + + Returns: + Provider instance or None if provider not found + """ + provider_class = PROVIDERS.get(provider_name.lower()) + if not provider_class: + return None + + return provider_class(api_key=api_key) \ No newline at end of file diff --git a/providers/__pycache__/__init__.cpython-310.pyc b/providers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ded32cca577eb692c5ad8f6078560bbf27177043 Binary files /dev/null and b/providers/__pycache__/__init__.cpython-310.pyc differ diff --git a/providers/__pycache__/deepseek.cpython-310.pyc b/providers/__pycache__/deepseek.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb1925c244049815bfcaa76cbde7be346f891bb0 Binary files /dev/null and b/providers/__pycache__/deepseek.cpython-310.pyc differ diff --git a/providers/__pycache__/huggingface.cpython-310.pyc b/providers/__pycache__/huggingface.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7d6162a439d4501f68ef7b67d454db7b9d7ba38 Binary files /dev/null and b/providers/__pycache__/huggingface.cpython-310.pyc differ diff --git a/providers/__pycache__/openai.cpython-310.pyc b/providers/__pycache__/openai.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69327bbf29e384da718d5821fe7f9bbc3e95a7e7 Binary files /dev/null and b/providers/__pycache__/openai.cpython-310.pyc differ diff --git a/providers/__pycache__/openrouter.cpython-310.pyc b/providers/__pycache__/openrouter.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c60263a2c88509c22795f0dbde8b9cdc3b1f78d Binary files /dev/null and b/providers/__pycache__/openrouter.cpython-310.pyc differ diff --git a/providers/deepseek.py b/providers/deepseek.py new file mode 100644 index 0000000000000000000000000000000000000000..5c5b83bdbd651117dda48a1db1c7f89f2bc083f7 --- /dev/null +++ b/providers/deepseek.py @@ -0,0 +1,210 @@ +""" +DeepSeek Provider Integration +Handles API calls to DeepSeek for AI model inference +""" +import os +import requests +import time +import json +import logging +from typing import Dict, Any, Optional, List + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("deepseek") + +class DeepSeekProvider: + """DeepSeek API provider for model inference""" + + def __init__(self, api_key: Optional[str] = None): + """Initialize the DeepSeek provider with API key""" + self.api_key = api_key or os.getenv("DEEPSEEK_API_KEY") + if not self.api_key: + logger.warning("No DeepSeek API key provided. Set DEEPSEEK_API_KEY env variable.") + + self.base_url = "https://api.deepseek.com/v1" + self.headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json" + } + + def generate_text(self, + prompt: str, + model: str = "deepseek-chat", + max_tokens: int = 1000, + temperature: float = 0.7, + system_message: str = "You are a helpful assistant.", + **kwargs) -> Dict[str, Any]: + """Generate text using DeepSeek models""" + if not self.api_key: + return {"success": False, "error": "DeepSeek API key not provided"} + + start_time = time.time() + + try: + messages = [ + {"role": "system", "content": system_message}, + {"role": "user", "content": prompt} + ] + + payload = { + "model": model, + "messages": messages, + "max_tokens": max_tokens, + "temperature": temperature, + **kwargs + } + + response = requests.post( + f"{self.base_url}/chat/completions", + headers=self.headers, + json=payload + ) + + # Check for errors + if response.status_code != 200: + logger.error(f"Error from DeepSeek API: {response.status_code} - {response.text}") + return { + "success": False, + "error": f"DeepSeek API error: {response.status_code}", + "response_time": time.time() - start_time, + "model": model, + "provider": "deepseek" + } + + result = response.json() + + # Extract the generated text + generated_text = result["choices"][0]["message"]["content"] + + return { + "success": True, + "text": generated_text, + "model": model, + "provider": "deepseek", + "response_time": time.time() - start_time, + "tokens": { + "prompt": result.get("usage", {}).get("prompt_tokens", 0), + "completion": result.get("usage", {}).get("completion_tokens", 0), + "total": result.get("usage", {}).get("total_tokens", 0) + }, + "raw_response": result + } + + except Exception as e: + logger.error(f"Error generating text with DeepSeek: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "deepseek" + } + + def generate_code(self, + prompt: str, + model: str = "deepseek-coder", + max_tokens: int = 2000, + temperature: float = 0.5, + **kwargs) -> Dict[str, Any]: + """Generate code using DeepSeek Coder models""" + if not self.api_key: + return {"success": False, "error": "DeepSeek API key not provided"} + + start_time = time.time() + + try: + messages = [ + {"role": "system", "content": "You are a helpful coding assistant."}, + {"role": "user", "content": prompt} + ] + + payload = { + "model": model, + "messages": messages, + "max_tokens": max_tokens, + "temperature": temperature, + **kwargs + } + + response = requests.post( + f"{self.base_url}/chat/completions", + headers=self.headers, + json=payload + ) + + # Check for errors + if response.status_code != 200: + logger.error(f"Error from DeepSeek API: {response.status_code} - {response.text}") + return { + "success": False, + "error": f"DeepSeek API error: {response.status_code}", + "response_time": time.time() - start_time, + "model": model, + "provider": "deepseek" + } + + result = response.json() + + # Extract the generated code + generated_code = result["choices"][0]["message"]["content"] + + return { + "success": True, + "text": generated_code, + "model": model, + "provider": "deepseek", + "response_time": time.time() - start_time, + "tokens": { + "prompt": result.get("usage", {}).get("prompt_tokens", 0), + "completion": result.get("usage", {}).get("completion_tokens", 0), + "total": result.get("usage", {}).get("total_tokens", 0) + }, + "raw_response": result + } + + except Exception as e: + logger.error(f"Error generating code with DeepSeek: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "deepseek" + } + + def get_available_models(self) -> List[Dict[str, Any]]: + """Get available DeepSeek models""" + if not self.api_key: + return [] + + # DeepSeek doesn't have a list models endpoint yet, so we hardcode the currently available models + models = [ + { + "id": "deepseek-chat", + "name": "DeepSeek Chat", + "description": "General-purpose language model for chat", + "context_length": 4096 + }, + { + "id": "deepseek-coder", + "name": "DeepSeek Coder", + "description": "Specialized model for code generation", + "context_length": 8192 + }, + { + "id": "deepseek-math", + "name": "DeepSeek Math", + "description": "Model fine-tuned for mathematical reasoning", + "context_length": 4096 + } + ] + + return models + +# Example usage +if __name__ == "__main__": + # Test the provider + provider = DeepSeekProvider() + result = provider.generate_text("Write a short poem about AI.") + print(json.dumps(result, indent=2)) \ No newline at end of file diff --git a/providers/huggingface.py b/providers/huggingface.py new file mode 100644 index 0000000000000000000000000000000000000000..560eda06608799739e1211f21902b4621f0bc6ef --- /dev/null +++ b/providers/huggingface.py @@ -0,0 +1,193 @@ +""" +Hugging Face Provider Integration +Handles API calls to Hugging Face for AI model inference +""" +import os +import requests +import time +import json +import logging +from typing import Dict, Any, Optional, List + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("huggingface") + +class HuggingFaceProvider: + """Hugging Face API provider for model inference""" + + def __init__(self, api_key: Optional[str] = None): + """Initialize the Hugging Face provider with API key""" + self.api_key = api_key or os.getenv("HUGGINGFACE_API_KEY") + if not self.api_key: + logger.warning("No Hugging Face API key provided. Set HUGGINGFACE_API_KEY env variable.") + + self.base_url = "https://api-inference.huggingface.co/models" + self.headers = {"Authorization": f"Bearer {self.api_key}"} if self.api_key else {} + + def generate_text(self, + prompt: str, + model: str = "mistralai/Mistral-7B-Instruct-v0.2", + max_tokens: int = 1000, + temperature: float = 0.7, + **kwargs) -> Dict[str, Any]: + """Generate text using Hugging Face text generation models""" + start_time = time.time() + + try: + url = f"{self.base_url}/{model}" + payload = { + "inputs": prompt, + "parameters": { + "max_new_tokens": max_tokens, + "temperature": temperature, + "return_full_text": False, + **kwargs + } + } + + response = requests.post( + url, + headers=self.headers, + json=payload + ) + + # Check for errors + if response.status_code != 200: + logger.error(f"Error from Hugging Face API: {response.status_code} - {response.text}") + return { + "success": False, + "error": f"Hugging Face API error: {response.status_code}", + "response_time": time.time() - start_time, + "model": model, + "provider": "huggingface" + } + + result = response.json() + + # Handle different response formats + generated_text = "" + if isinstance(result, list) and len(result) > 0: + if "generated_text" in result[0]: + generated_text = result[0]["generated_text"] + else: + generated_text = result[0].get("text", "") + elif "generated_text" in result: + generated_text = result["generated_text"] + + return { + "success": True, + "text": generated_text, + "model": model, + "provider": "huggingface", + "response_time": time.time() - start_time, + "raw_response": result + } + + except Exception as e: + logger.error(f"Error generating text with Hugging Face: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "huggingface" + } + + def generate_image(self, + prompt: str, + model: str = "stabilityai/stable-diffusion-xl-base-1.0", + height: int = 512, + width: int = 512, + **kwargs) -> Dict[str, Any]: + """Generate image using Hugging Face image generation models""" + start_time = time.time() + + try: + url = f"{self.base_url}/{model}" + payload = { + "inputs": prompt, + "parameters": { + "height": height, + "width": width, + **kwargs + } + } + + response = requests.post( + url, + headers=self.headers, + json=payload + ) + + # Image response is binary + if response.status_code != 200: + logger.error(f"Error from Hugging Face API: {response.status_code} - {response.text}") + return { + "success": False, + "error": f"Hugging Face API error: {response.status_code}", + "response_time": time.time() - start_time, + "model": model, + "provider": "huggingface" + } + + # Return binary image data in base64 + import base64 + image_data = base64.b64encode(response.content).decode("utf-8") + + return { + "success": True, + "image_data": image_data, + "model": model, + "provider": "huggingface", + "response_time": time.time() - start_time + } + + except Exception as e: + logger.error(f"Error generating image with Hugging Face: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "huggingface" + } + + def get_available_models(self, task: str = "text-generation") -> List[Dict[str, Any]]: + """Get available models for a specific task""" + try: + url = "https://huggingface.co/api/models" + params = { + "filter": task, + "sort": "downloads", + "direction": -1, + "limit": 100 + } + + response = requests.get(url, params=params) + + if response.status_code != 200: + logger.error(f"Error fetching models: {response.status_code} - {response.text}") + return [] + + models = response.json() + return [ + { + "id": model["id"], + "name": model.get("name", model["id"]), + "downloads": model.get("downloads", 0), + "tags": model.get("tags", []) + } + for model in models + ] + + except Exception as e: + logger.error(f"Error fetching models: {e}") + return [] + +# Example usage +if __name__ == "__main__": + # Test the provider + provider = HuggingFaceProvider() + result = provider.generate_text("Write a short poem about AI.") + print(json.dumps(result, indent=2)) \ No newline at end of file diff --git a/providers/openai.py b/providers/openai.py new file mode 100644 index 0000000000000000000000000000000000000000..088b7d46ff98ad8f9063c8015ab3fedaf9aee410 --- /dev/null +++ b/providers/openai.py @@ -0,0 +1,178 @@ +""" +OpenAI Provider Integration +Handles API calls to OpenAI for text and image generation +""" +import os +import time +import json +import logging +from typing import Dict, Any, Optional, List + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("openai") + +try: + import openai + from openai import OpenAI + HAS_OPENAI = True +except ImportError: + logger.warning("OpenAI package not installed. Install with: pip install openai") + HAS_OPENAI = False + +class OpenAIProvider: + """OpenAI API provider for model inference""" + + def __init__(self, api_key: Optional[str] = None): + """Initialize the OpenAI provider with API key""" + if not HAS_OPENAI: + logger.error("OpenAI package not installed. Install with: pip install openai") + return + + self.api_key = api_key or os.getenv("OPENAI_API_KEY") + if not self.api_key: + logger.warning("No OpenAI API key provided. Set OPENAI_API_KEY env variable.") + + # Initialize client + self.client = OpenAI(api_key=self.api_key) + + def generate_text(self, + prompt: str, + model: str = "gpt-3.5-turbo", + max_tokens: int = 1000, + temperature: float = 0.7, + system_message: str = "You are a helpful assistant.", + **kwargs) -> Dict[str, Any]: + """Generate text using OpenAI models""" + if not HAS_OPENAI or not self.api_key: + return {"success": False, "error": "OpenAI package not installed or API key not provided"} + + start_time = time.time() + + try: + messages = [ + {"role": "system", "content": system_message}, + {"role": "user", "content": prompt} + ] + + response = self.client.chat.completions.create( + model=model, + messages=messages, + max_tokens=max_tokens, + temperature=temperature, + **kwargs + ) + + # Extract the generated text + generated_text = response.choices[0].message.content + + return { + "success": True, + "text": generated_text, + "model": model, + "provider": "openai", + "response_time": time.time() - start_time, + "tokens": { + "prompt": response.usage.prompt_tokens, + "completion": response.usage.completion_tokens, + "total": response.usage.total_tokens + }, + "raw_response": response.model_dump() + } + + except Exception as e: + logger.error(f"Error generating text with OpenAI: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "openai" + } + + def generate_image(self, + prompt: str, + model: str = "dall-e-3", + size: str = "1024x1024", + quality: str = "standard", + n: int = 1, + **kwargs) -> Dict[str, Any]: + """Generate image using OpenAI DALL-E models""" + if not HAS_OPENAI or not self.api_key: + return {"success": False, "error": "OpenAI package not installed or API key not provided"} + + start_time = time.time() + + try: + response = self.client.images.generate( + model=model, + prompt=prompt, + size=size, + quality=quality, + n=n, + **kwargs + ) + + return { + "success": True, + "image_url": response.data[0].url, # URL of the generated image + "model": model, + "provider": "openai", + "response_time": time.time() - start_time, + "raw_response": response.model_dump() + } + + except Exception as e: + logger.error(f"Error generating image with OpenAI: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "openai" + } + + def get_available_models(self) -> List[Dict[str, Any]]: + """Get available OpenAI models""" + if not HAS_OPENAI or not self.api_key: + return [] + + try: + response = self.client.models.list() + + models = [ + { + "id": model.id, + "name": model.id, + "created": model.created + } + for model in response.data + ] + + # Filter to only include completion and chat models + text_models = [ + model for model in models + if any(prefix in model["id"] for prefix in ["gpt-", "text-"]) + ] + + # Add DALL-E models (they don't show up in the list) + image_models = [ + {"id": "dall-e-3", "name": "DALL-E 3"}, + {"id": "dall-e-2", "name": "DALL-E 2"} + ] + + return { + "text_models": text_models, + "image_models": image_models + } + + except Exception as e: + logger.error(f"Error fetching OpenAI models: {e}") + return [] + +# Example usage +if __name__ == "__main__": + # Test the provider + provider = OpenAIProvider() + result = provider.generate_text("Write a short poem about AI.") + print(json.dumps(result, indent=2)) \ No newline at end of file diff --git a/providers/openrouter.py b/providers/openrouter.py new file mode 100644 index 0000000000000000000000000000000000000000..bd131520f5f2d4af71b1a8d3ef043b75b6c583e6 --- /dev/null +++ b/providers/openrouter.py @@ -0,0 +1,164 @@ +""" +OpenRouter Provider Integration +Handles API calls to OpenRouter for AI model inference across multiple providers +""" +import os +import requests +import time +import json +import logging +from typing import Dict, Any, Optional, List + +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("openrouter") + +class OpenRouterProvider: + """OpenRouter API provider for model inference across multiple AI providers""" + + def __init__(self, api_key: Optional[str] = None): + """Initialize the OpenRouter provider with API key""" + self.api_key = api_key or os.getenv("OPENROUTER_API_KEY") + if not self.api_key: + logger.warning("No OpenRouter API key provided. Set OPENROUTER_API_KEY env variable.") + + self.base_url = "https://openrouter.ai/api/v1" + self.headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + "HTTP-Referer": os.getenv("APP_URL", "http://localhost:8000"), # Required by OpenRouter + "X-Title": os.getenv("APP_NAME", "AI Tool Hub") # Your app name + } + + def generate_text(self, + prompt: str, + model: str = "anthropic/claude-3-opus:beta", + max_tokens: int = 1000, + temperature: float = 0.7, + system_message: str = "You are a helpful assistant.", + **kwargs) -> Dict[str, Any]: + """Generate text using OpenRouter models""" + if not self.api_key: + return {"success": False, "error": "OpenRouter API key not provided"} + + start_time = time.time() + + try: + messages = [ + {"role": "system", "content": system_message}, + {"role": "user", "content": prompt} + ] + + payload = { + "model": model, + "messages": messages, + "max_tokens": max_tokens, + "temperature": temperature, + **kwargs + } + + response = requests.post( + f"{self.base_url}/chat/completions", + headers=self.headers, + json=payload + ) + + # Check for errors + if response.status_code != 200: + logger.error(f"Error from OpenRouter API: {response.status_code} - {response.text}") + return { + "success": False, + "error": f"OpenRouter API error: {response.status_code}", + "response_time": time.time() - start_time, + "model": model, + "provider": "openrouter" + } + + result = response.json() + + # Extract the generated text + generated_text = result["choices"][0]["message"]["content"] + + return { + "success": True, + "text": generated_text, + "model": model, + "provider": "openrouter", + "response_time": time.time() - start_time, + "tokens": { + "prompt": result.get("usage", {}).get("prompt_tokens", 0), + "completion": result.get("usage", {}).get("completion_tokens", 0), + "total": result.get("usage", {}).get("total_tokens", 0) + }, + "raw_response": result + } + + except Exception as e: + logger.error(f"Error generating text with OpenRouter: {e}") + return { + "success": False, + "error": str(e), + "response_time": time.time() - start_time, + "model": model, + "provider": "openrouter" + } + + def get_available_models(self) -> List[Dict[str, Any]]: + """Get available OpenRouter models""" + if not self.api_key: + return [] + + try: + response = requests.get( + f"{self.base_url}/models", + headers=self.headers + ) + + if response.status_code != 200: + logger.error(f"Error fetching OpenRouter models: {response.status_code} - {response.text}") + return [] + + models_data = response.json() + models = [] + + for model in models_data.get("data", []): + models.append({ + "id": model.get("id"), + "name": model.get("name", model.get("id")), + "description": model.get("description", ""), + "context_length": model.get("context_length", 4096), + "pricing": { + "prompt": model.get("pricing", {}).get("prompt", 0), + "completion": model.get("pricing", {}).get("completion", 0) + } + }) + + return models + + except Exception as e: + logger.error(f"Error fetching OpenRouter models: {e}") + return [] + + def get_models_by_provider(self, provider: str = None) -> List[Dict[str, Any]]: + """Get available models filtered by provider""" + models = self.get_available_models() + + if not provider: + return models + + return [model for model in models if provider.lower() in model.get("id", "").lower()] + +# Example usage +if __name__ == "__main__": + # Test the provider + provider = OpenRouterProvider() + result = provider.generate_text("Write a short poem about AI.") + print(json.dumps(result, indent=2)) + + # Get all models + models = provider.get_available_models() + print(f"Found {len(models)} models") + + # Get only Anthropic models + anthropic_models = provider.get_models_by_provider("anthropic") + print(f"Found {len(anthropic_models)} Anthropic models") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..4d992e5f5cc181bc57bb8bcfc8ad846214846842 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,35 @@ +# Web Framework +fastapi==0.115.0 +uvicorn==0.24.0 +jinja2==3.1.2 +python-multipart==0.0.6 +pydantic==2.4.2 +python-dotenv==1.0.0 +itsdangerous==2.1.2 +aiofiles==23.2.1 +requests==2.31.0 +# starlette is included as a dependency of FastAPI + +# Database +boto3==1.28.48 +redis==5.0.0 + +# AI/ML +openai==1.0.0 + +# Authentication +python-jose[cryptography]>=3.3.0 +passlib[bcrypt]>=1.7.4 +PyJWT==2.8.0 + +# Utilities +pyyaml>=6.0.0 +psutil==5.9.5 + +# Testing +pytest>=7.0.0 +pytest-asyncio>=0.18.0 + +# Additional dependencies +pandas==2.1.0 +huggingface-hub==0.17.2 \ No newline at end of file diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..162beb89e1fcc742adfb7702fdaefb1d4402fe08 --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,737 @@ +/* MegicAI - CSS styles + * Main stylesheet for the single-process FastAPI application + */ + +/* === Base Styles === */ +:root { + --primary-color: #121212; + --secondary-color: #7367F0; + --accent-color: #FFD700; + --text-color: #FFFFFF; + --text-dark: #333333; + --card-bg: rgba(30, 30, 40, 0.85); + --hover-color: rgba(255, 255, 255, 0.15); + --sidebar-bg: rgba(21, 21, 31, 0.95); + --sidebar-hover: rgba(255, 255, 255, 0.1); + --input-bg: rgba(45, 45, 60, 0.8); + --button-primary-bg: linear-gradient(135deg, #7367F0, #8E54E9); + --button-secondary-bg: rgba(70, 70, 85, 0.5); + --header-bg: rgba(25, 25, 35, 0.9); + --tooltip-bg: rgba(10, 10, 15, 0.95); + --dark-sidebar: #1a1a1a; + --success-color: #4CAF50; + --warning-color: #FFC107; + --error-color: #F44336; + --info-color: #2196F3; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + line-height: 1.6; + color: var(--text-color); + background-color: var(--primary-color); + min-height: 100vh; +} + +a { + color: var(--text-color); + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + margin-bottom: 0.5rem; + font-weight: 600; +} + +/* === Layout === */ +.app-container { + display: flex; + min-height: 100vh; +} + +/* === Sidebar === */ +.sidebar { + width: 260px; + background-color: var(--sidebar-bg); + border-right: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + flex-direction: column; + position: fixed; + height: 100vh; + overflow-y: auto; +} + +.sidebar-header { + padding: 20px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.sidebar-header h1 { + font-size: 1.5rem; + margin-bottom: 0.2rem; +} + +.sidebar-subtitle { + opacity: 0.7; + font-size: 0.9rem; +} + +.sidebar-section { + padding: 20px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.sidebar-heading { + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 1px; + opacity: 0.7; + margin-bottom: 15px; +} + +.nav-item { + display: flex; + align-items: center; + padding: 10px; + border-radius: 5px; + margin-bottom: 5px; + transition: background-color 0.2s; +} + +.nav-item:hover { + background-color: var(--sidebar-hover); +} + +.nav-item.active { + background-color: var(--secondary-color); +} + +.nav-icon { + margin-right: 12px; + font-size: 1.2rem; +} + +.sidebar-footer { + margin-top: auto; + padding: 20px; + opacity: 0.5; + font-size: 0.8rem; + text-align: center; +} + +/* === Content Area === */ +.content { + flex: 1; + padding: 30px; + margin-left: 260px; + width: calc(100% - 260px); +} + +.content-header { + margin-bottom: 30px; +} + +.content-header h1 { + font-size: 2rem; + margin-bottom: 0.5rem; +} + +/* === Cards & Tools === */ +.tools-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; +} + +.tool-card { + background-color: var(--card-bg); + border-radius: 8px; + padding: 20px; + display: flex; + transition: transform 0.2s, box-shadow 0.2s; + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.tool-card:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); +} + +.tool-icon { + font-size: 2rem; + margin-right: 15px; + display: flex; + align-items: center; + justify-content: center; +} + +.tool-info { + flex: 1; +} + +.tool-name { + font-size: 1.2rem; + margin-bottom: 5px; +} + +.tool-description { + opacity: 0.8; + font-size: 0.9rem; + margin-bottom: 10px; +} + +.tool-meta { + display: flex; + align-items: center; + margin-top: auto; +} + +.tool-cost { + background-color: rgba(255, 215, 0, 0.2); + color: var(--accent-color); + padding: 2px 8px; + border-radius: 20px; + font-size: 0.8rem; +} + +/* === Credits Display === */ +.credit-display { + display: flex; + align-items: center; + background-color: rgba(255, 255, 255, 0.1); + padding: 12px; + border-radius: 8px; +} + +/* === Auth Buttons & User Info === */ +.auth-buttons { + display: flex; + gap: 10px; + margin-bottom: 15px; +} + +.auth-buttons .btn { + flex: 1; +} + +.user-section { + margin-bottom: 15px; +} + +.user-info { + display: flex; + align-items: center; + margin-bottom: 12px; +} + +.user-icon { + font-size: 1.5rem; + margin-right: 12px; + color: var(--secondary-color); +} + +.user-name { + font-weight: 600; +} + +.user-role { + font-size: 0.8rem; + opacity: 0.7; +} + +.user-actions { + display: flex; + gap: 10px; +} + +.btn-small { + padding: 5px 10px; + font-size: 0.8rem; +} + +.credit-icon { + font-size: 1.5rem; + margin-right: 12px; +} + +.credit-label { + font-size: 0.8rem; + opacity: 0.8; +} + +.credit-value { + font-size: 1.2rem; + font-weight: 600; +} + +/* === Forms & Inputs === */ +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + margin-bottom: 8px; + font-weight: 500; +} + +.form-select { + width: 100%; + padding: 10px; + background-color: var(--input-bg); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 5px; + color: var(--text-color); + font-size: 1rem; +} + +.prompt-textarea { + width: 100%; + min-height: 150px; + padding: 12px; + background-color: var(--input-bg); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 5px; + color: var(--text-color); + font-size: 1rem; + resize: vertical; +} + +.template-box { + background-color: rgba(0, 0, 0, 0.2); + padding: 12px; + border-radius: 5px; + margin-bottom: 10px; + font-size: 0.9rem; +} + +/* === Buttons === */ +.btn { + padding: 10px 20px; + border-radius: 5px; + border: none; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s; + display: inline-block; + text-align: center; +} + +.btn-primary { + background: var(--button-primary-bg); + color: white; +} + +.btn-primary:hover { + opacity: 0.9; + transform: translateY(-2px); +} + +.btn-secondary { + background-color: var(--button-secondary-bg); + color: white; + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.btn-secondary:hover { + background-color: rgba(90, 90, 105, 0.5); +} + +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* === Search === */ +.search-container { + margin-bottom: 20px; +} + +.search-input { + width: 100%; + padding: 12px 15px; + border-radius: 5px; + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: var(--input-bg); + color: var(--text-color); + font-size: 1rem; +} + +/* === Tool Page === */ +.tool-header { + display: flex; + align-items: center; + margin-bottom: 30px; + background-color: var(--header-bg); + padding: 20px; + border-radius: 10px; +} + +.tool-header-icon { + font-size: 2.5rem; + margin-right: 20px; +} + +.tool-content { + display: grid; + grid-template-columns: 3fr 1fr; + gap: 20px; +} + +.tool-sidebar { + background-color: var(--card-bg); + border-radius: 8px; + padding: 20px; + height: fit-content; +} + +.tool-cost-box h3 { + margin-bottom: 10px; +} + +.cost-display { + background-color: rgba(255, 215, 0, 0.2); + padding: 10px; + border-radius: 5px; + text-align: center; + margin-bottom: 15px; +} + +.cost-value { + font-size: 1.5rem; + font-weight: 600; + color: var(--accent-color); +} + +.suggestion-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 20px; +} + +.suggestion-chip { + background-color: var(--button-secondary-bg); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 20px; + padding: 5px 15px; + font-size: 0.9rem; + cursor: pointer; + transition: all 0.2s; +} + +.suggestion-chip:hover { + background-color: var(--secondary-color); +} + +.button-section { + display: flex; + gap: 10px; + margin-top: 30px; +} + +/* === Result Page === */ +.result-header { + margin-bottom: 20px; +} + +.result-meta { + display: flex; + gap: 20px; + margin-top: 10px; +} + +.result-provider, .result-time { + display: flex; + align-items: center; + background-color: rgba(255, 255, 255, 0.1); + padding: 8px 15px; + border-radius: 20px; + font-size: 0.9rem; +} + +.provider-icon, .time-icon { + margin-right: 8px; +} + +.result-content { + background-color: var(--card-bg); + border-radius: 10px; + padding: 20px; + margin-bottom: 20px; +} + +.result-text { + background-color: white; + color: var(--text-dark); + padding: 20px; + border-radius: 8px; + margin-bottom: 20px; + line-height: 1.6; +} + +.result-image img { + max-width: 100%; + border-radius: 8px; + display: block; + margin: 0 auto; +} + +.result-video video { + max-width: 100%; + border-radius: 8px; + display: block; + margin: 0 auto; +} + +.result-ai-probability { + margin-top: 20px; +} + +.probability-bar { + height: 20px; + background-color: rgba(255, 255, 255, 0.1); + border-radius: 10px; + overflow: hidden; + margin-bottom: 5px; +} + +.probability-fill { + height: 100%; + width: var(--probability); + background: linear-gradient(90deg, #4CAF50, #F44336); + border-radius: 10px; +} + +.probability-label { + display: flex; + justify-content: space-between; + font-size: 0.8rem; + opacity: 0.8; +} + +.result-actions { + display: flex; + flex-direction: column; + gap: 20px; +} + +.action-buttons { + display: flex; + gap: 10px; +} + +.result-details { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 5px; + padding: 10px; +} + +.result-details summary { + cursor: pointer; + padding: 5px; +} + +.response-json { + background-color: rgba(0, 0, 0, 0.3); + padding: 10px; + border-radius: 5px; + overflow-x: auto; + margin-top: 10px; +} + +.response-json pre { + font-family: monospace; + font-size: 0.9rem; + white-space: pre-wrap; +} + +/* === Ad Page === */ +.ad-header { + margin-bottom: 20px; +} + +.ad-content { + background-color: var(--card-bg); + border-radius: 10px; + padding: 20px; +} + +.ad-info { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-bottom: 20px; +} + +.ad-reward, .ad-duration { + display: flex; + align-items: center; + background-color: rgba(0, 0, 0, 0.2); + padding: 15px; + border-radius: 8px; +} + +.reward-icon, .duration-icon { + font-size: 2rem; + margin-right: 15px; +} + +.ad-player { + margin-bottom: 20px; +} + +.ad-container { + background-color: #000; + aspect-ratio: 16 / 9; + border-radius: 8px; + overflow: hidden; +} + +.mock-ad { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: white; + text-align: center; +} + +.mock-ad-content { + max-width: 80%; +} + +.ad-timer-container { + margin: 20px 0; +} + +.ad-timer { + display: inline-block; + background-color: rgba(255, 255, 255, 0.2); + padding: 5px 15px; + border-radius: 20px; + font-weight: bold; +} + +.mock-ad-image { + margin-top: 20px; + background-color: rgba(255, 255, 255, 0.1); + aspect-ratio: 16 / 9; + border-radius: 5px; + display: flex; + align-items: center; + justify-content: center; +} + +.ad-actions { + display: flex; + gap: 10px; + margin-top: 20px; +} + +/* === Error Page === */ +.error-container { + background-color: var(--card-bg); + border-radius: 10px; + padding: 30px; +} + +.error-header { + display: flex; + align-items: center; + margin-bottom: 20px; +} + +.error-icon { + font-size: 3rem; + color: var(--error-color); + margin-right: 20px; +} + +.error-code { + font-size: 0.9rem; + opacity: 0.7; +} + +.error-message { + background-color: rgba(244, 67, 54, 0.1); + border-left: 4px solid var(--error-color); + padding: 15px; + margin-bottom: 20px; +} + +.error-suggestions { + margin-bottom: 20px; +} + +.error-suggestions ul { + margin-left: 20px; +} + +.error-actions { + display: flex; + gap: 10px; +} + +/* === Alerts === */ +.alert { + padding: 15px; + border-radius: 5px; + margin-bottom: 20px; +} + +.alert-success { + background-color: rgba(76, 175, 80, 0.1); + border-left: 4px solid var(--success-color); + color: var(--success-color); +} + +.alert-warning { + background-color: rgba(255, 193, 7, 0.1); + border-left: 4px solid var(--warning-color); + color: var(--warning-color); +} + +.alert-error { + background-color: rgba(244, 67, 54, 0.1); + border-left: 4px solid var(--error-color); + color: var(--error-color); +} + +/* === Responsive Design === */ +@media (max-width: 768px) { + .app-container { + flex-direction: column; + } + + .sidebar { + width: 100%; + height: auto; + position: relative; + } + + .content { + margin-left: 0; + width: 100%; + } + + .tool-content { + grid-template-columns: 1fr; + } + + .tools-grid { + grid-template-columns: 1fr; + } + + .ad-info { + grid-template-columns: 1fr; + } +} \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000000000000000000000000000000000000..3b36035061461ef22f7aff8569932c6f3531982e --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,260 @@ +/** + * MegicAI - Main JavaScript + * Common functionality used across the application + */ + +// Helper function to format numbers with commas +function formatNumber(num) { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} + +// Update credit display with animation +function updateCredits(newValue, animate = true) { + const creditDisplay = document.querySelector('.credit-value'); + if (!creditDisplay) return; + + const currentValue = parseInt(creditDisplay.textContent.replace(/,/g, ''), 10); + + if (animate && !isNaN(currentValue)) { + const diff = newValue - currentValue; + const duration = 1000; // 1 second animation + const startTime = performance.now(); + + function updateCounter(timestamp) { + const elapsed = timestamp - startTime; + const progress = Math.min(elapsed / duration, 1); + const currentCount = Math.floor(currentValue + (diff * progress)); + + creditDisplay.textContent = formatNumber(currentCount); + + if (progress < 1) { + requestAnimationFrame(updateCounter); + } else { + creditDisplay.textContent = formatNumber(newValue); + } + } + + requestAnimationFrame(updateCounter); + } else { + creditDisplay.textContent = formatNumber(newValue); + } +} + +// Copy text to clipboard +function copyToClipboard(text, successCallback = null) { + navigator.clipboard.writeText(text) + .then(() => { + if (successCallback) successCallback(); + }) + .catch(err => { + console.error('Failed to copy text: ', err); + }); +} + +// Toggle visibility of an element +function toggleVisibility(elementId) { + const element = document.getElementById(elementId); + if (element) { + element.style.display = element.style.display === 'none' ? 'block' : 'none'; + } +} + +// Add suggestions to a textarea +function addSuggestionToPrompt(suggestion, elementId) { + const textarea = document.getElementById(elementId); + if (textarea) { + const currentText = textarea.value; + textarea.value = currentText ? `${currentText}\n${suggestion}` : suggestion; + textarea.focus(); + } +} + +// Form validation +function validateForm(formId, errorElementId = null) { + const form = document.getElementById(formId); + if (!form) return true; + + let isValid = true; + const requiredInputs = form.querySelectorAll('[required]'); + + // Clear previous error messages + form.querySelectorAll('.field-error').forEach(el => el.remove()); + + requiredInputs.forEach(input => { + if (!input.value.trim()) { + isValid = false; + + // Create error message + const errorMsg = document.createElement('div'); + errorMsg.className = 'field-error'; + errorMsg.textContent = 'This field is required'; + input.parentNode.appendChild(errorMsg); + + // Add error style to input + input.classList.add('input-error'); + } else { + input.classList.remove('input-error'); + } + }); + + // If there's a global error element, update it + if (!isValid && errorElementId) { + const errorElement = document.getElementById(errorElementId); + if (errorElement) { + errorElement.textContent = 'Please fill in all required fields'; + errorElement.style.display = 'block'; + } + } + + return isValid; +} + +// Handle form submission with AJAX +function submitFormAsync(formId, successCallback, errorCallback) { + const form = document.getElementById(formId); + if (!form) return; + + form.addEventListener('submit', function(event) { + event.preventDefault(); + + if (!validateForm(formId)) return; + + const formData = new FormData(form); + const submitButton = form.querySelector('button[type="submit"]'); + + if (submitButton) { + const originalText = submitButton.innerHTML; + submitButton.disabled = true; + submitButton.innerHTML = ' Processing...'; + } + + fetch(form.action, { + method: form.method, + body: formData + }) + .then(response => { + // Check if the response is JSON or HTML + const contentType = response.headers.get('content-type'); + if (!response.ok) { + if (contentType && contentType.includes('application/json')) { + return response.json().then(data => { + throw new Error(data.message || 'An error occurred'); + }); + } else { + // For non-JSON errors, just show the status text instead of trying to parse JSON + throw new Error('Server error occurred: ' + response.statusText); + } + } + + // If response is HTML, handle it as a redirect + if (contentType && contentType.includes('text/html')) { + // This is an HTML response, likely a result page - redirect to it + window.location.href = response.url; + return { success: true, redirected: true }; + } + + // Check if it's a redirect (e.g., 302, 303) + if (response.redirected) { + window.location.href = response.url; + return { success: true, redirected: true }; + } + + // Otherwise process as JSON + if (contentType && contentType.includes('application/json')) { + return response.json(); + } else { + // If it's not JSON and not HTML with a redirect, handle it as a success + return { success: true, message: "Operation completed successfully" }; + } + }) + .then(data => { + if (successCallback) successCallback(data); + }) + .catch(error => { + console.error("Error during form submission:", error); + if (errorCallback) errorCallback(error.message); + }) + .finally(() => { + if (submitButton) { + submitButton.disabled = false; + submitButton.innerHTML = originalText; + } + }); + }); +} + +// Document ready event +document.addEventListener('DOMContentLoaded', function() { + console.log('MegicAI application initialized'); + + // Initialize any forms with async submission + const asyncForms = document.querySelectorAll('[data-async-submit]'); + asyncForms.forEach(form => { + const formId = form.id; + const successTarget = form.getAttribute('data-success-target'); + const errorTarget = form.getAttribute('data-error-target'); + + // Check if the form is for HTML-based operations like process-request + const actionUrl = form.getAttribute('action'); + if (actionUrl && (actionUrl.includes('/process-request') || actionUrl.includes('/watch-ad'))) { + // These endpoints return HTML, not JSON - use regular form submission + console.log('Form will use direct submission:', actionUrl); + form.removeAttribute('data-async-submit'); + + // Add regular submit handler with validation + form.addEventListener('submit', function(event) { + if (!validateForm(formId)) { + event.preventDefault(); + return false; + } + + const submitButton = form.querySelector('button[type="submit"]'); + if (submitButton) { + // Add loading indicator + const originalText = submitButton.innerHTML; + submitButton.disabled = true; + submitButton.innerHTML = ' Processing...'; + + // Make sure the form submits normally for HTML responses + setTimeout(() => { + if (submitButton.disabled) { + // Re-enable after a timeout just in case + submitButton.disabled = false; + submitButton.innerHTML = originalText; + } + }, 10000); // 10 second timeout + } + + return true; + }); + } else { + // Use async submission for JSON-based API endpoints + submitFormAsync( + formId, + data => { + if (successTarget) { + const targetElement = document.getElementById(successTarget); + if (targetElement) { + targetElement.textContent = data.message || 'Success!'; + targetElement.style.display = 'block'; + } + } + + // If a redirect URL is provided in the response + if (data.redirect) { + window.location.href = data.redirect; + } + }, + error => { + if (errorTarget) { + const targetElement = document.getElementById(errorTarget); + if (targetElement) { + targetElement.textContent = error; + targetElement.style.display = 'block'; + } + } + } + ); + } + }); +}); \ No newline at end of file diff --git a/templates/ad.html b/templates/ad.html new file mode 100644 index 0000000000000000000000000000000000000000..acd73c523bf5386b4f43e5a9c762da2240407294 --- /dev/null +++ b/templates/ad.html @@ -0,0 +1,160 @@ + + + + + + {{ app_name }} - Watch Ad + + + + +
+ + + + +
+
+

Watch an Ad to Earn Credits

+

Watch this ad to gain free credits for using AI tools.

+
+ +
+
+

Ad Information

+
+ +
+
+
+ +
+
+

Reward

+

{{ ad_reward }} credits

+
+
+ +
+
+ +
+
+

Duration

+

{{ ad_duration }} seconds

+
+
+
+ +
+
+
+
+

Advertisement

+

This is a sample advertisement. In a real implementation, this would be an actual ad from an ad network.

+
+ +
+
+
+
+ +
+
+ {{ ad_duration }} seconds remaining +
+
+
+ +
+ + + + + +
+ + Cancel +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/ad_reward.html b/templates/ad_reward.html new file mode 100644 index 0000000000000000000000000000000000000000..de6afb08fb51fce2837ebfb36de34f7a657b95ab --- /dev/null +++ b/templates/ad_reward.html @@ -0,0 +1,226 @@ +{% extends "base.html" %} + +{% block title %}Watch Ad & Earn Credits{% endblock %} + +{% block content %} +
+
+
+

+ Watch Ad & Earn Credits +

+
+ +
+
+
+
+

How it works

+

+ Watch a short advertisement to earn credits that you can use to generate content with our AI tools. +

+
    +
  • Complete the ad to earn {{ tool.cost }} credits
  • +
  • You will be redirected back to the tool automatically
  • +
  • Your credits will be available immediately
  • +
+
+ + +
+
+
+ + + + +
+

Loading advertisement...

+ +
+ + + + +
+
+ +
+
+

Your Balance

+
+
+ + + + + {{ user_credits }} +
+
+ + Credits + +
+
+
+ + +
+

Tool Information

+
+ {{ tool.name }} +
+
+ Cost: + {{ tool.cost }} credits +
+

+ {{ tool.description }} +

+
+ + +
+ {% if sidebar_ad and sidebar_ad.success %} + {{ sidebar_ad.ad_code|safe }} + {% endif %} +
+
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/templates/admin_dashboard.html b/templates/admin_dashboard.html new file mode 100644 index 0000000000000000000000000000000000000000..992beb165ee8ef5338beb874647a94f7403313bb --- /dev/null +++ b/templates/admin_dashboard.html @@ -0,0 +1,238 @@ +{% extends "base.html" %} + +{% block title %}Admin Dashboard{% endblock %} + +{% block content %} +
+
+

Admin Dashboard

+

Manage users and credits

+
+ +
+
+
+

User Statistics

+
+
+
+
+
Total Users
+
{{ total_users }}
+
+
+
Admin Users
+
{{ admin_count }}
+
+
+
Total Credits Issued
+
{{ total_credits }}
+
+
+
+
+ +
+
+

Quick Actions

+
+
+
+ + + + +
+
+
+
+ +
+
+

User Management

+
+ +
+
+
+ + + + + + + + + + + {% for user in users %} + + + + + + + {% endfor %} + +
+ User + + Role + + Credits + + Actions +
+
+
+ {{ user.username }} +
+
+ {{ user.email }} +
+
+
+ + {{ user.role }} + + + {{ user.credits }} + + + + | + +
+
+
+
+ + + +{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000000000000000000000000000000000000..fe3ce2233e95b7757a6da51e7457ba7ec5dacf2a --- /dev/null +++ b/templates/base.html @@ -0,0 +1,217 @@ + + + + + + {{ app_name }} - {% block title %}{% endblock %} + + + + + + + + + + + + +
+ +
+
+

Tools

+ +
+
+ + +
+ {% block content %}{% endblock %} +
+
+ + +
+ + + + + {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/templates/credits.html b/templates/credits.html new file mode 100644 index 0000000000000000000000000000000000000000..d1828b5f6011bacda90ea9b8c6d9c0a91ea66add --- /dev/null +++ b/templates/credits.html @@ -0,0 +1,296 @@ +{% extends "base.html" %} + +{% block title %}Credits{% endblock %} + +{% block content %} +
+ +
+
+

Credits

+

Manage your credits for using AI tools

+
+
+
{{ user_credits }}
+
credits available
+
+
+ +
+ +
+

Earn Credits

+ + +
+

Daily Rewards

+

+ Watch ads to earn up to 30 credits per day (3 rewards of 10 credits each) +

+
+
+ 0/3 rewards claimed today +
+ +
+ +
+ + +
+

Special Reward

+

+ Watch a longer ad to earn 25 bonus credits (available once per day) +

+ +
+ + +
+

Referral Program

+

+ Invite friends to earn 50 credits for each referral +

+
+ + +
+
+
+ + +
+

Purchase Credits

+ + +
+ {% for package in credit_packages %} +
+
+
+

{{ package.name }}

+

{{ package.description }}

+
+
+
${{ package.price }}
+
{{ package.credits }} credits
+
+
+ +
+ {% endfor %} +
+ + +
+

Accepted Payment Methods

+
+ + + + + + + + + +
+
+
+
+ + +
+

Credit History

+
+
+ + + + + + + + + + + {% for transaction in credit_history %} + + + + + + + {% endfor %} + +
DateTypeAmountDetails
+ {{ transaction.date }} + + {{ transaction.type }} + + {{ transaction.amount }} + + {{ transaction.details }} +
+
+
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000000000000000000000000000000000000..2f8da13775b29b54b3e82c1954bcc79061b8b186 --- /dev/null +++ b/templates/error.html @@ -0,0 +1,87 @@ + + + + + + {{ app_name }} - Error + + + + +
+ + + + +
+
+

Error

+

Something went wrong with your request.

+
+ +
+
+ +
+

{{ error_title if error_title is defined else 'Error Occurred' }}

+

{{ error_description if error_description is defined else 'An unexpected error occurred while processing your request.' }}

+ +
+ Return to Home + +
+
+
+
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000000000000000000000000000000000000..44819df4c36821a514dafbf3d9b8311fc241c742 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,211 @@ + + + + + + {{ app_name }} - AI Tools Platform + + + + + +
+ + + + +
+
+

AI Tools Platform

+

Select a tool to get started. Each tool requires credits which you can earn by watching ads.

+ +
+ +
+
+ + + + + + +
+
+ + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000000000000000000000000000000000000..0f23afc1648d5712cfd3c9c98358b5ec6ceab5f3 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,104 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+

+ Sign in to your account +

+

+ Or + + create a new account + +

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+ + +
+ +
+ +
+
+ +
+
+
+
+
+
+ + Or continue with + +
+
+ + +
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/marketplace.html b/templates/marketplace.html new file mode 100644 index 0000000000000000000000000000000000000000..6d59d5e5d47abbfaf9005d2f6475b250ff018888 --- /dev/null +++ b/templates/marketplace.html @@ -0,0 +1,287 @@ +{% extends "base.html" %} + +{% block content %} +
+
+ +
+
+

Prompt Marketplace

+

Discover and purchase high-quality prompts for various AI tools

+
+
+
{{ user_credits }}
+
credits available
+
+
+ + +
+
+ +
+
+ +
+ + + +
+
+
+ + +
+ +
+ + +
+ +
+
+
+ + +
+ {% for prompt in prompts %} +
+ +
+
+
+

{{ prompt.title }}

+

{{ prompt.description }}

+
+
+ + {{ "%.1f"|format(prompt.avg_rating) }} +
+
+ + +
+ {% for tag in prompt.tags %} + + {{ tag }} + + {% endfor %} +
+ + +
+ + + + + {{ prompt.usage_count }} uses +
+
+ + +
+
+ {{ prompt.content }} +
+
+ + +
+
+
+ Created by {{ prompt.creator_id }} +
+ +
+
+
+ {% endfor %} +
+ + + +
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/register.html b/templates/register.html new file mode 100644 index 0000000000000000000000000000000000000000..060348243b9b73e7db6f38b0ed512a570fb539ac --- /dev/null +++ b/templates/register.html @@ -0,0 +1,93 @@ + + + + + + Register | {{ app_name }} + + + + + +
+
+
+

{{ app_name }}

+
Create an Account
+
+ + {% if error %} +
+ + {{ error }} +
+ {% endif %} + +
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+ +
+
+
or
+
+
+ + + + +
+
+ + \ No newline at end of file diff --git a/templates/result.html b/templates/result.html new file mode 100644 index 0000000000000000000000000000000000000000..c96118ab6ad4113ec334a08582ebad7f178af717 --- /dev/null +++ b/templates/result.html @@ -0,0 +1,166 @@ + + + + + + {{ app_name }} - Result + + + + +
+ + + + +
+
+

Generated Result

+

Here's what our AI has created for you.

+
+ +
+

{{ result.tool_name }}

+
+
+
+
{{ result.provider }}
+
+
+
+
{{ result.response_time }}s
+
+
+
+ +
+ {% if result.type == 'text' %} +
+
+

Your Prompt:

+

{{ result.prompt }}

+
+
+

AI Response:

+
+ {{ result.result|safe }} +
+
+
+ {% elif result.type == 'image' %} +
+
+

Your Prompt:

+

{{ result.prompt }}

+
+
+

Generated Image:

+ Generated image +
+
+ {% elif result.type == 'code' %} +
+
+

Your Prompt:

+

{{ result.prompt }}

+
+
+

Generated Code:

+
{{ result.result }}
+
+
+ {% endif %} + + {% if result.ai_probability is defined %} +
+

AI Detection Probability

+
+
+
+
+
Human-like
+
{{ result.ai_probability }}%
+
AI-generated
+
+
+ {% endif %} +
+ +
+ + +
+ View Response Details +
+ {% if json_data is defined %} +
{{ json_data|tojson(indent=2) }}
+ {% else %} +
No detailed response data available
+ {% endif %} +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/tool.html b/templates/tool.html new file mode 100644 index 0000000000000000000000000000000000000000..e622d6137584fcdaec164547474434bbe16cc4dc --- /dev/null +++ b/templates/tool.html @@ -0,0 +1,179 @@ + + + + + + {{ tool.name }} | {{ app_name }} + + + + + +
+ + + + +
+
+
+ +
+
+

{{ tool.name }}

+

{{ tool.description }}

+
+
+ +
+
+

Enter your prompt:

+
+ + +
+ +
+ +
+ + {% for suggestion in suggestions %} +
{{ suggestion }}
+ {% endfor %} +
+ +
+ + +
+ +
+ + +
+ + + + +
+ + Back to Tools +
+
+
+ +
+
+

Credit Cost

+
+
{{ tool.cost }}
+
credits per generation
+
+ +
+

Your Balance

+
+
+ +
+
+
Available
+
{{ user_credits }}
+
+
+
+ + {% if user_credits < tool.cost %} +
+ + You need {{ tool.cost - user_credits }} more credits +
+ + Watch {{ tool.ad_duration }}s Ad (+ {{ tool.cost }} credits) + + {% endif %} +
+
+
+
+
+ + \ No newline at end of file diff --git a/test_app.py b/test_app.py new file mode 100644 index 0000000000000000000000000000000000000000..656afa9bd55190a9b66485cda9142fcf2221b9b5 --- /dev/null +++ b/test_app.py @@ -0,0 +1,45 @@ +from fastapi import FastAPI, Request +from fastapi.responses import HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates + +app = FastAPI() + +# Mount static files +app.mount("/static", StaticFiles(directory="static"), name="static") + +# Setup templates +templates = Jinja2Templates(directory="templates") + +@app.get("/", response_class=HTMLResponse) +async def home(request: Request): + return templates.TemplateResponse( + "index.html", + { + "request": request, + "app_name": "MegicAI Test", + "user_credits": 100, + "tools": [ + { + "id": "text_gen", + "name": "Text Generator", + "description": "Generate creative text", + "icon": "fas fa-font", + "category": "text", + "credits": 5 + }, + { + "id": "image_gen", + "name": "Image Creator", + "description": "Create images from text", + "icon": "fas fa-image", + "category": "image", + "credits": 10 + } + ] + } + ) + +@app.get("/hello") +def read_root(): + return {"message": "Hello World"} \ No newline at end of file diff --git a/tools.py b/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..ad9a3624e746ef37c935b6f0facbe49576fee39a --- /dev/null +++ b/tools.py @@ -0,0 +1,342 @@ +class Tool: + def __init__(self, id, name, description, icon, cost, example_prompts, providers): + self.id = id + self.name = name + self.description = description + self.icon = icon + self.cost = cost + self.example_prompts = example_prompts + self.providers = providers + + def get_info(self): + """Return a summary of the tool's information.""" + return { + "id": self.id, + "name": self.name, + "description": self.description, + "icon": self.icon, + "cost": self.cost, + "example_prompts": self.example_prompts, + "providers": self.providers, + } + +# Define the tools with associated prompts +TOOLS = [ + Tool( + id="text-generation", + name="Text Generation", + description="Generate text using AI models", + icon="text.svg", + cost=1.0, + example_prompts=[ + "Write a short story about a robot learning to paint.", + "Create a product description for a new smartphone.", + "Explain quantum computing to a 10-year-old." + ], + providers=["openai", "deepseek", "openrouter", "huggingface"] + ), + Tool( + id="image-generation", + name="Image Generation", + description="Generate images from text descriptions", + icon="image.svg", + cost=5.0, + example_prompts=[ + "A futuristic city with flying cars and tall buildings.", + "A photorealistic portrait of a cyberpunk character.", + "A peaceful mountain landscape at sunset." + ], + providers=["openai", "huggingface"] + ), + Tool( + id="code-generation", + name="Code Generation", + description="Generate code in various programming languages", + icon="code.svg", + cost=2.0, + example_prompts=[ + "Write a Python function to calculate Fibonacci numbers.", + "Create a React component for a login form.", + "Generate a SQL query to find customers who made purchases last month." + ], + providers=["openai", "deepseek", "openrouter"] + ), + Tool( + id="ai-copywriter", + name="AI Copywriter", + description="Generates product descriptions and creative content.", + icon="copywriter.svg", + cost=0.20, + example_prompts=[ + "Generate a product description for a new gadget.", + "Create a catchy tagline for a marketing campaign." + ], + providers=["huggingface", "openrouter"] + ), + Tool( + id="email-generator", + name="Email Generator", + description="Drafts professional or marketing emails.", + icon="email.svg", + cost=0.18, + example_prompts=[ + "Draft a follow-up email for a job application.", + "Create a marketing email for a new product launch." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="blog-writer", + name="Blog Writer", + description="Writes detailed blog posts with SEO optimization.", + icon="blog.svg", + cost=0.30, + example_prompts=[ + "Write a blog post about the benefits of meditation.", + "Create a travel blog post about Paris." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="resume-builder", + name="AI Resume Builder", + description="Creates professional resumes tailored to job roles.", + icon="resume.svg", + cost=0.25, + example_prompts=[ + "Create a resume for a software engineer.", + "Draft a resume for a marketing manager." + ], + providers=["huggingface", "openrouter"] + ), + Tool( + id="cover-letter-creator", + name="Cover Letter Creator", + description="Generates customized cover letters.", + icon="cover_letter.svg", + cost=0.20, + example_prompts=[ + "Generate a cover letter for a data analyst position.", + "Create a cover letter for a graphic designer role." + ], + providers=["huggingface", "openrouter"] + ), + Tool( + id="script-generator", + name="Script Generator", + description="Writes engaging video or movie scripts.", + icon="script.svg", + cost=0.30, + example_prompts=[ + "Write a script for a 5-minute promotional video.", + "Create a movie script for a romantic comedy." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="storytelling", + name="AI Storytelling", + description="Develops creative and compelling stories.", + icon="storytelling.svg", + cost=0.20, + example_prompts=[ + "Create a fantasy story about a dragon.", + "Write a mystery story set in a small town." + ], + providers=["huggingface", "openrouter"] + ), + Tool( + id="chatbot-assistant", + name="Chatbot Assistant", + description="Provides conversational responses for support.", + icon="chatbot.svg", + cost=0.18, + example_prompts=[ + "Provide support for a customer inquiry.", + "Answer frequently asked questions about a product." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="ai-image-generator", + name="AI Image Generator", + description="Creates visuals from text prompts.", + icon="ai_image.svg", + cost=2.50, + example_prompts=[ + "Generate an image of a sunset over the mountains.", + "Create an illustration of a futuristic city." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-logo-creator", + name="AI Logo Creator", + description="Designs logos for startups and businesses.", + icon="logo.svg", + cost=1.50, + example_prompts=[ + "Create a logo for a new tech startup.", + "Design a logo for a coffee shop." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-avatar-generator", + name="AI Avatar Generator", + description="Generates custom avatars for gaming or profiles.", + icon="avatar.svg", + cost=2.00, + example_prompts=[ + "Generate an avatar for a gaming profile.", + "Create a custom avatar for a social media account." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-face-swap", + name="AI Face Swap", + description="Swaps faces in images or videos seamlessly.", + icon="face_swap.svg", + cost=2.00, + example_prompts=[ + "Swap faces in a family photo.", + "Create a fun face swap for a video." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-meme-creator", + name="AI Meme Creator", + description="Generates memes with custom captions.", + icon="meme.svg", + cost=0.40, + example_prompts=[ + "Create a meme about cats.", + "Generate a funny meme for social media." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-video-editor", + name="AI Video Editor", + description="Automates video edits, transitions, and effects.", + icon="video_editor.svg", + cost=10.00, + example_prompts=[ + "Edit a video for a YouTube channel.", + "Create a highlight reel from a sports event." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-video-script-writer", + name="AI Video Script Writer", + description="Generates structured video content ideas.", + icon="video_script.svg", + cost=0.30, + example_prompts=[ + "Write a script for a cooking tutorial.", + "Create a script for a travel vlog." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="ai-video-dubbing", + name="AI Video Dubbing", + description="Dubs videos in multiple languages with natural voices.", + icon="video_dubbing.svg", + cost=5.00, + example_prompts=[ + "Dub a video in Spanish.", + "Create a multilingual version of a promotional video." + ], + providers=["huggingface", "replicate"] + ), + Tool( + id="ai-data-analyzer", + name="AI Data Analyzer", + description="Analyzes datasets for insights and trends.", + icon="data_analyzer.svg", + cost=3.00, + example_prompts=[ + "Analyze sales data for trends.", + "Generate insights from customer feedback data." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="ai-code-optimizer", + name="AI Code Optimizer", + description="Enhances Python, JavaScript, and SQL code.", + icon="code_optimizer.svg", + cost=0.60, + example_prompts=[ + "Optimize this Python function for performance.", + "Improve the readability of this SQL query." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="ai-debugging-assistant", + name="AI Debugging Assistant", + description="Identifies and corrects coding errors.", + icon="debugging.svg", + cost=1.00, + example_prompts=[ + "Find and fix errors in this JavaScript code.", + "Debug this Python script for syntax issues." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="ai-quiz-generator", + name="AI Quiz Generator", + description="Creates quizzes with multiple-choice options.", + icon="quiz.svg", + cost=0.40, + example_prompts=[ + "Generate a quiz on world history.", + "Create a multiple-choice quiz for a science topic." + ], + providers=["huggingface", "deepseek"] + ), + Tool( + id="ai-poetry-generator", + name="AI Poetry Generator", + description="Crafts unique poetry on various themes.", + icon="poetry.svg", + cost=0.20, + example_prompts=[ + "Write a poem about love.", + "Create a haiku about nature." + ], + providers=["huggingface", "openrouter"] + ), + Tool( + id="ai-meditation-coach", + name="AI Meditation Coach", + description="Guides users through relaxation exercises.", + icon="meditation.svg", + cost=1.00, + example_prompts=[ + "Guide a user through a breathing exercise.", + "Provide a meditation session for stress relief." + ], + providers=["huggingface", "replicate"] + ) +] + +def get_tool_by_id(tool_id): + """Retrieve a tool by its ID.""" + for tool in TOOLS: + if tool.id == tool_id: + return tool.get_info() + return None + +def get_tool_by_id(tool_id): + """Retrieve a tool by its ID.""" + for tool in TOOLS: + if tool.id == tool_id: + return tool.get_info() + return None \ No newline at end of file