from pydantic import BaseModel, EmailStr, Field from typing import Optional, List, Dict, Any from datetime import datetime from enum import Enum import uuid # ─── Enums ──────────────────────────────────────────────────────────────────── class PlanType(str, Enum): free = "free" starter = "starter" pro = "pro" enterprise = "enterprise" class SubscriptionStatus(str, Enum): active = "active" canceled = "canceled" past_due = "past_due" unpaid = "unpaid" trialing = "trialing" class ChatbotVisibility(str, Enum): preview = "preview" published = "published" class DocumentStatus(str, Enum): pending = "pending" processing = "processing" completed = "completed" failed = "failed" class MessageRole(str, Enum): user = "user" assistant = "assistant" system = "system" # ─── Auth Models ────────────────────────────────────────────────────────────── class UserSignup(BaseModel): email: EmailStr password: str = Field(min_length=8) company_name: str = Field(min_length=2, max_length=100) class UserLogin(BaseModel): email: EmailStr password: str class UserResponse(BaseModel): id: str email: str company_name: Optional[str] = None plan: str = "free" created_at: Optional[datetime] = None class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" user: UserResponse # ─── Company Models ──────────────────────────────────────────────────────────── class CompanyCreate(BaseModel): name: str = Field(min_length=2, max_length=100) website: Optional[str] = None industry: Optional[str] = None class CompanyUpdate(BaseModel): name: Optional[str] = None website: Optional[str] = None industry: Optional[str] = None logo_url: Optional[str] = None class CompanyResponse(BaseModel): id: str owner_id: str name: str website: Optional[str] = None industry: Optional[str] = None logo_url: Optional[str] = None created_at: Optional[datetime] = None # ─── Chatbot Models ──────────────────────────────────────────────────────────── class ChatbotCreate(BaseModel): name: str = Field(min_length=2, max_length=100) description: Optional[str] = None system_prompt: Optional[str] = None model: str = "accounts/fireworks/models/kimi-k2-instruct-0905" temperature: float = Field(default=0.7, ge=0.0, le=2.0) max_tokens: int = Field(default=1000, ge=100, le=8000) primary_color: str = "#6366f1" welcome_message: str = "Hello! How can I help you today?" logo_url: Optional[str] = None category: Optional[str] = None industry: Optional[str] = None languages: List[str] = ["en"] class ChatbotUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None system_prompt: Optional[str] = None model: Optional[str] = None temperature: Optional[float] = None max_tokens: Optional[int] = None primary_color: Optional[str] = None welcome_message: Optional[str] = None logo_url: Optional[str] = None category: Optional[str] = None industry: Optional[str] = None languages: Optional[List[str]] = None class ChatbotResponse(BaseModel): id: str company_id: str name: str description: Optional[str] = None system_prompt: Optional[str] = None model: str temperature: float max_tokens: int primary_color: str welcome_message: str logo_url: Optional[str] = None category: Optional[str] = None industry: Optional[str] = None languages: List[str] visibility: str is_published: bool qdrant_collection_name: Optional[str] = None document_count: int = 0 conversation_count: int = 0 average_rating: Optional[float] = None created_at: Optional[datetime] = None published_at: Optional[datetime] = None class ChatbotPublicResponse(BaseModel): """For marketplace display""" id: str name: str description: Optional[str] = None category: Optional[str] = None industry: Optional[str] = None languages: List[str] primary_color: str welcome_message: str logo_url: Optional[str] = None average_rating: Optional[float] = None total_conversations: int = 0 company_name: Optional[str] = None company_logo: Optional[str] = None created_at: Optional[datetime] = None published_at: Optional[datetime] = None # ─── Document Models ─────────────────────────────────────────────────────────── class DocumentResponse(BaseModel): id: str chatbot_id: str file_name: str file_type: str file_size: int chunk_count: int status: str error_message: Optional[str] = None created_at: Optional[datetime] = None # ─── Chat Models ─────────────────────────────────────────────────────────────── class ChatMessage(BaseModel): message: str = Field(min_length=1, max_length=4000) session_id: Optional[str] = None language: str = "en" class SourceDocument(BaseModel): document_name: str chunk_text: str score: float page_number: Optional[int] = None class ChatResponse(BaseModel): response: str session_id: str sources: List[SourceDocument] = [] model_used: str tokens_used: int = 0 class MessageResponse(BaseModel): id: str role: str content: str sources: Optional[List[Dict]] = None created_at: Optional[datetime] = None class ConversationResponse(BaseModel): id: str chatbot_id: str session_id: Optional[str] = None language: str message_count: int created_at: Optional[datetime] = None messages: List[MessageResponse] = [] # ─── Subscription Models ─────────────────────────────────────────────────────── class SubscriptionResponse(BaseModel): id: str user_id: str plan: str status: str stripe_customer_id: Optional[str] = None current_period_start: Optional[datetime] = None current_period_end: Optional[datetime] = None chatbots_published: int = 0 conversations_used: int = 0 created_at: Optional[datetime] = None class CheckoutSessionCreate(BaseModel): plan: str # starter or pro success_url: str cancel_url: str class CheckoutSessionResponse(BaseModel): checkout_url: str session_id: str # ─── Analytics Models ────────────────────────────────────────────────────────── class ChatbotAnalytics(BaseModel): chatbot_id: str total_conversations: int = 0 unique_users: int = 0 average_conversation_length: float = 0.0 total_messages: int = 0 average_rating: float = 0.0 top_queries: List[str] = [] conversations_last_7_days: List[Dict] = [] conversations_last_30_days: int = 0 # ─── Marketplace Models ──────────────────────────────────────────────────────── class MarketplaceFilter(BaseModel): category: Optional[str] = None industry: Optional[str] = None language: Optional[str] = None search: Optional[str] = None page: int = 1 limit: int = 20 class MarketplaceResponse(BaseModel): chatbots: List[ChatbotPublicResponse] total: int page: int limit: int has_more: bool class RatingCreate(BaseModel): rating: int = Field(ge=1, le=5) feedback: Optional[str] = None # ─── Code Export Models ──────────────────────────────────────────────────────── class CodeExportRequest(BaseModel): chatbot_id: str include_frontend: bool = True # ─── Generic Response Models ─────────────────────────────────────────────────── class MessageDetail(BaseModel): detail: str class SuccessResponse(BaseModel): success: bool message: str class ErrorResponse(BaseModel): error: str detail: Optional[str] = None