Create Chatbot Service - Complete Developer Documentation¶
Service: Chatbot Project Creation & Management
Port: 8003
Purpose: Create chatbot projects, manage quotas, track chatbot states
Technology: FastAPI (Python 3.9+)
Code Location:/create-chatbot-service/src/main.py(521 lines, 11 endpoints)
Owner: Backend Team
Last Updated: 2025-12-26
Table of Contents¶
- Service Overview
- Architecture & Responsibilities
- Complete Endpoints
- Project ID Generation
- Quota Management System
- Email Notification System
- State Tracking System
- Database Operations
- Database Type Selection
- Security Analysis
- Deployment
Service Overview¶
The Create Chatbot Service handles the creation of new chatbot projects and manages the entire chatbot lifecycle from creation to state tracking. It enforces subscription quotas and sends email notifications when users approach their limits.
Key Responsibilities¶
✅ Project Creation - Generate unique project IDs
✅ Quota Management - Enforce chatbot limits based on subscription
✅ Email Notifications - Warn users about quota limits
✅ State Tracking - Track chatbot setup progress (3D, Text, Voice)
✅ Incomplete Project Detection - Return existing incomplete projects
✅ Database Type Selection - Choose between CosmosDB/Milvus
Statistics¶
- Total Lines: 521
- Endpoints: 11
- Dependencies: FastAPI, PyMongo, Azure Communication Email
- Collections Used: 4 (projectid_creation, selection_history, features_per_user, users_multichatbot_v2)
Architecture & Responsibilities¶
Position in System¶
graph TB
subgraph "Client"
A[Frontendfile]
end
subgraph "Gateway"
B[Gateway<br/>Port 8000]
end
subgraph "Services"
C[Create Chatbot<br/>Port 8003]
D[User Service<br/>Port 8002]
end
subgraph "Database"
E[(Cosmos DB)]
end
subgraph "External"
F[Azure Email]
end
A -->|POST /v2/create-chatbot| B
B --> C
C -->|Check quota| E
C -->|Create project| E
C -->|Send email| F
style C fill:#E1F5FE
Workflow Example¶
User Creates First Chatbot:
- Frontend → Gateway → Create Chatbot Service
- Check existing count (0 chatbots)
- Check user's limit from
features_per_user(Free plan = 1) - Generate project_id:
User-123456_Project_1 - Insert into
projectid_creation - Insert into
selection_history - Return project_id to user
User Reaches Limit:
- User tries to create 2nd chatbot (limit = 1)
- Service detects quota exceeded
- Send "Quota Exceeded" email
- Return error to user
Complete Endpoints¶
1. POST /v2/create-chatbot¶
Purpose: Create new chatbot project (checks for incomplete projects first)
Code Location: Lines 245-313
Request:
async def create_chatbot(
user_id: str = Form(...),
database_type: str = Form(None) # Optional: "cosmosdb" or "milvus"
)
Example:
curl -X POST http://localhost:8003/v2/create-chatbot \
-F "user_id=User-123456" \
-F "database_type=milvus"
Response (New Chatbot):
Response (Existing Incomplete Chatbot):
2. POST /v2/create-chatbot1¶
Purpose: Create chatbot with dynamic state checking (3D/Text/Voice)
Code Location: Lines 316-398
Difference from /v2/create-chatbot:
- V1: Only checks 3D chatbot state
- V2: Checks state based on
chatbot_type(3D, Text, or Voice)
3. GET /v2/get-chatbots¶
Purpose: Get all chatbots for a user (excludes deleted)
Code Location: Lines 402-423
Request:
Response:
{
"user_id": "User-123456",
"projects": [
{
"project_id": "User-123456_Project_1",
"active": true
},
{
"project_id": "User-123456_Project_2",
"active": false
}
]
}
Database Query:
db.projectid_creation.find(
{
user_id: "User-123456",
$or: [{ isDeleted: { $exists: false } }, { isDeleted: false }],
},
{
_id: 0,
project_id: 1,
active: 1,
}
);
4. GET /v2/get-chatbot-status¶
Purpose: Check if a specific chatbot is active
Code Location: Lines 427-448
Request:
Response:
5. POST /v2/chatbot_name¶
Purpose: Set chatbot name
Code Location: Lines 452-464
Request:
async def create_chatbot(
user_id: str = Form(...),
project_id: str = Form(...),
chatbot_name: str = Form(...)
)
6. GET /v2/get-chatbot-name¶
Purpose: Get chatbot names for a project
Code Location: Lines 466-484
7. GET /v2/get-database-selection¶
Purpose: Get database type (CosmosDB/Milvus) for a project
Code Location: Lines 486-517
Response:
{
"user_id": "User-123456",
"project_id": "User-123456_Project_1",
"database_type": "milvus",
"is_default": false
}
Project ID Generation¶
Generation Logic¶
Function: generate_project_id(user_id, existing_count)
Code Location: Lines 64-65
Examples¶
| User ID | Existing Count | Generated Project ID |
|---|---|---|
User-123456 |
0 | User-123456_Project_1 |
User-123456 |
1 | User-123456_Project_2 |
User-123456 |
5 | User-123456_Project_6 |
Count Calculation¶
Code (Line 265):
Example:
// User has 2 existing chatbots
db.projectid_creation.count_documents({ user_id: "User-123456" });
// Returns: 2
// Generate 3rd project ID
generate_project_id("User-123456", 2); // Returns: "User-123456_Project_3"
Quota Management System¶
Overview¶
The service enforces chatbot creation limits based on user subscriptions. Free users get 1 chatbot, paid plans get more.
Get User Limit¶
Function: get_user_chatbot_limit(user_id)
Code Location: Lines 68-84
def get_user_chatbot_limit(user_id: str) -> int:
"""Get the chatbot limit for a user from features_per_user collection"""
try:
user_features = features_per_user_collection.find_one({"user_id": user_id})
if user_features and "no_of_chatbots" in user_features:
limit = int(user_features["no_of_chatbots"])
logger.debug(f"Chatbot limit for user {user_id}: {limit}")
return limit
else:
logger.warning(f"No chatbot limit found for user {user_id}, using default: 1")
return 1
except (ValueError, TypeError) as e:
logger.warning(f"Error converting chatbot limit for user {user_id}: {e}")
return 1
except Exception as e:
logger.error(f"Error fetching chatbot limit for user {user_id}: {e}")
return 1
Database Query:
Result:
{
"_id": ObjectId("..."),
"user_id": "User-123456",
"subscription_id": "sub_009", // Free plan
"no_of_chatbots": 1, // LIMIT
"no_of_chat_sessions": 50,
// ... other features
}
Subscription Limits¶
| Subscription | Chatbot Limit | Notes |
|---|---|---|
| Free (sub_009) | 1 | Single chatbot |
| Starter | 3 | Small teams |
| Professional | 10 | Growing businesses |
| Business | 50 | Enterprises |
| Enterprise | Unlimited | Custom |
Quota Check Logic (NOT IMPLEMENTED YET!)¶
⚠️ IMPORTANT DISCOVERY: The quota checking code exists (lines 87-175) but is NOT called in the create endpoints!
Missing Implementation:
# This should be added to create_chatbot() around line 265
chatbot_count = projectid_collection.count_documents({"user_id": user_id})
user_limit = get_user_chatbot_limit(user_id)
if chatbot_count >= user_limit:
# User exceeded quota
send_quota_exceeded_email(user_id, chatbot_count, user_limit)
raise HTTPException(
status_code=403,
detail=f"Chatbot limit exceeded. You have {chatbot_count}/{user_limit} chatbots."
)
elif chatbot_count == user_limit - 1:
# User is creating their last allowed chatbot
send_quota_warning_email(user_id, chatbot_count + 1, user_limit)
Email Notification System¶
Configuration¶
⚠️ HARDCODED CREDENTIALS (Lines 56-61):
EMAIL_ENDPOINT = "https://mailing-sevice.india.communication.azure.com/"
EMAIL_ACCESS_KEY = "CgdEWi6fBCJv4..." # Hardcoded!
SENDER_EMAIL = "DoNotReply@machineagents.ai"
email_client = EmailClient(endpoint=EMAIL_ENDPOINT, credential=EMAIL_ACCESS_KEY)
Same security issue as User Service!
1. Quota Warning Email¶
Function: send_quota_warning_email(user_id, current_count, limit)
Code Location: Lines 87-127
Triggered When: User creates their last allowed chatbot
Example:
- User limit: 3 chatbots
- User creates 3rd chatbot
- Email sent: "You've reached 3/3 chatbots"
Email Content:
Subject: Chatbot Quota Limit Notification
Body:
Dear User,
You have just created your 3th chatbot out of your allowed limit of 3 chatbots.
This means you have reached your maximum chatbot creation limit according to your current subscription plan. If you need to create more chatbots, please consider upgrading your subscription plan.
For any assistance, please contact our support team.
Thank you for using our service!
Best regards,
The Machine Agents Team
Code (Lines 99-119):
message = {
"senderAddress": SENDER_EMAIL,
"recipients": {"to": [{"address": user_email}]},
"content": {
"subject": "Chatbot Quota Limit Notification",
"plainText": f"""
Dear User,
You have just created your {current_count}th chatbot out of your allowed limit of {limit} chatbots.
This means you have reached your maximum chatbot creation limit according to your current subscription plan. If you need to create more chatbots, please consider upgrading your subscription plan.
For any assistance, please contact our support team.
Thank you for using our service!
Best regards,
The Machine Agents Team
"""
}
}
email_client.begin_send(message)
2. Quota Exceeded Email¶
Function: send_quota_exceeded_email(user_id, current_count, limit)
Code Location: Lines 130-175
Triggered When: User tries to create chatbot after exceeding limit
Example:
- User limit: 1 chatbot
- User already has 1 chatbot
- User tries to create 2nd chatbot → BLOCKED + Email sent
Email Content:
Subject: Chatbot Creation Failed - Quota Exceeded
Body:
Dear User,
Your attempt to create a new chatbot has been blocked because you have already reached your maximum limit.
Current Status:
- Chatbots Created: 1
- Maximum Allowed: 1
- Remaining: 0
To create more chatbots, please upgrade your subscription plan. Our premium plans offer higher chatbot limits and additional features.
For assistance with upgrading your plan or any questions, please contact our support team.
Thank you for using our service!
Best regards,
The Machine Agents Team
State Tracking System¶
The service tracks chatbot setup progress through multiple steps. Different chatbot types (3D, Text, Voice) have different completion states.
3D Chatbot States¶
Function: check_selection_state_3D(user_id, project_id)
Code Location: Lines 178-199
States:
| State | Required Fields | Description |
|---|---|---|
| start | None | Chatbot created, no configuration |
| chatbot_type_selected | chatbot_type |
User selected "3D-chatbot" |
| sitemap_urls_fetched | + sitemap_urls |
Website URLs fetched |
| chatbot_purpose_selected | + chatbot_purpose |
Purpose selected (Support, Sales, etc.) |
| avatar_selected | + selection_avatar |
3D avatar chosen |
| END | + selection_voice |
All steps complete! |
Code:
def check_selection_state_3D(user_id: str, project_id: str):
selection = selection_collection.find_one({"user_id": user_id, "project_id": project_id})
if not selection or selection.get("chatbot_type") != "3D-chatbot":
return {"state": "start", "message": "No chatbot collection found."}
elif "chatbot_type" in selection and "sitemap_urls" not in selection:
return {"state": "chatbot_type_selected"}
elif "chatbot_type" in selection and "sitemap_urls" in selection and "chatbot_purpose" not in selection:
return {"state": "sitemap_urls_fetched"}
elif ... and "selection_avatar" not in selection:
return {"state": "chatbot_purpose_selected"}
elif ... and "selection_voice" not in selection:
return {"state": "avatar_selected"}
elif ... and "selection_voice" in selection:
return {"state": "END"} # Final step completed
else:
return {"state": "start"}
Text Chatbot States¶
Function: check_selection_state_text(user_id, project_id)
Code Location: Lines 202-220
States:
| State | Required Fields | Description |
|---|---|---|
| start | None | Chatbot created |
| chatbot_type_selected | chatbot_type |
"text-chatbot" selected |
| sitemap_urls_fetched | + sitemap_urls |
URLs fetched |
| END | + chatbot_purpose |
Complete (no avatar/voice needed) |
Simpler than 3D: No avatar or voice selection needed!
Voice Chatbot States¶
Function: check_selection_state_voice(user_id, project_id)
Code Location: Lines 222-243
States:
| State | Required Fields | Description |
|---|---|---|
| start | None | Chatbot created |
| chatbot_type_selected | chatbot_type |
"voice-chatbot" selected |
| sitemap_urls_fetched | + sitemap_urls |
URLs fetched |
| chatbot_purpose_selected | + chatbot_purpose |
Purpose selected |
| END | + selection_voice |
Complete (no avatar needed) |
State-Based Project Return¶
Logic in /v2/create-chatbot1 (Lines 339-366):
# Check if last project is incomplete
if chatbot_count > 0:
last_project_id = generate_project_id(user_id, chatbot_count)
existing_project = projectid_collection.find_one({"user_id": user_id, "project_id": last_project_id})
if existing_project:
# Get chatbot type
selection = selection_collection.find_one({"user_id": user_id, "project_id": last_project_id})
if selection and "chatbot_type" in selection:
chatbot_type = selection["chatbot_type"]
# Call appropriate state check
if chatbot_type == "text-chatbot":
state_response = check_selection_state_text(user_id, project_id)
elif chatbot_type == "voice-chatbot":
state_response = check_selection_state_voice(user_id, project_id)
elif chatbot_type == "3D-chatbot":
state_response = check_selection_state_3D(user_id, project_id)
# If not complete, return existing project
if state_response["state"] != "END":
return JSONResponse(
content={"message": "Chatbot created", "project_id": last_project_id},
status_code=201
)
Result: Users can't create multiple incomplete chatbots - they must finish setup first!
Database Operations¶
Collections Used¶
- projectid_creation - Main chatbot projects
- selection_history - Chatbot configuration state
- features_per_user - User subscription limits
- users_multichatbot_v2 - User email for notifications
1. projectid_creation Collection¶
Purpose: Store all chatbot projects
Schema:
{
"_id": ObjectId("..."),
"user_id": "User-123456",
"project_id": "User-123456_Project_1",
"state": "IN_PROGRESS", // or "COMPLETED"
"created_at": ISODate("2025-01-15T10:00:00Z"),
"active": true,
"database_type": "milvus", // or "cosmosdb"
"isDeleted": false // Soft delete flag
}
Insert Operation (Lines 286-293):
projectid_collection.insert_one({
"user_id": user_id,
"project_id": project_id,
"state": "IN_PROGRESS",
"created_at": datetime.now(),
"active": True,
"database_type": database_type
})
2. selection_history Collection¶
Purpose: Track chatbot configuration progress
Schema:
{
"_id": ObjectId("..."),
"user_id": "User-123456",
"project_id": "User-123456_Project_1",
// Database selection
"database_type": "milvus",
"database_selection_timestamp": ISODate("2025-01-15T10:00:05Z"),
// Configuration steps (populated over time)
"chatbot_type": "3D-chatbot", // or "text-chatbot", "voice-chatbot"
"chatbot_purpose": "Customer Support",
"selection_avatar": "Avatar_Emma",
"selection_voice": "en-US-JennyNeural",
"selection_model": "openai-35",
"sitemap_urls": ["https://example.com/page1", "https://example.com/page2"],
// Optional
"chatbot_name": "Support Bot",
"domain": "https://example.com"
}
Upsert Operation (Lines 296-303):
selection_collection.update_one(
{"user_id": user_id, "project_id": project_id},
{"$set": {
"database_type": database_type,
"database_selection_timestamp": datetime.utcnow()
}},
upsert=True # Create if doesn't exist
)
3. features_per_user Collection¶
Purpose: Get user's chatbot limit
Query (Line 71):
user_features = features_per_user_collection.find_one({"user_id": user_id})
limit = int(user_features["no_of_chatbots"])
Example Document:
{
"user_id": "User-123456",
"subscription_id": "sub_009",
"no_of_chatbots": 1, // ← THIS IS THE LIMIT
"no_of_chat_sessions": 50,
// ... other features
}
4. users_multichatbot_v2 Collection¶
Purpose: Get user email for notifications
Query (Line 91):
Database Type Selection¶
CosmosDB vs Milvus¶
The service supports two vector database options:
Configuration¶
Environment Variable:
Code (Lines 42, 253-263):
default_database = os.getenv("DEFAULT_DATABASE", "cosmosdb").lower()
# In endpoint
if database_type is None:
database_type = default_database # Use env default
else:
database_type = database_type.lower()
if database_type not in ["cosmosdb", "milvus"]:
raise HTTPException(
status_code=400,
detail="Invalid database_type. Must be 'cosmosdb' or 'milvus'"
)
Comparison¶
| Feature | CosmosDB | Milvus |
|---|---|---|
| Type | Document DB | Vector DB |
| Embeddings | Stored in documents | Native vector index |
| Search | MongoDB queries | Optimized vector search |
| Cost | Higher (Azure pricing) | Lower (self-hosted) |
| Scalability | Auto-scaling | Manual scaling |
| Current Default | CosmosDB | Milvus (per env) |
Storage¶
Database type stored in 2 places:
- projectid_creation collection:
- selection_history collection:
{
"project_id": "User-123456_Project_1",
"database_type": "milvus",
"database_selection_timestamp": ISODate("...")
}
Security Analysis¶
Critical Issues¶
1. ⚠️ HARDCODED EMAIL CREDENTIALS
Location: Lines 56-58
Same issue as User Service!
Fix:
EMAIL_ENDPOINT = os.getenv("AZURE_EMAIL_ENDPOINT")
EMAIL_ACCESS_KEY = os.getenv("AZURE_EMAIL_ACCESS_KEY")
2. ⚠️ QUOTA CHECKING NOT IMPLEMENTED
Problem: Functions exist but are never called!
Impact:
- Users can exceed their chatbot limits
- No emails sent when limits reached
- Subscription limits not enforced
Fix: Add quota check to create endpoints (shown in Quota Management section above)
3. ⚠️ NO RATE LIMITING
Problem: User can create chatbots rapidly
Fix:
from slowapi import Limiter
@app.post("/v2/create-chatbot")
@limiter.limit("10/minute") # 10 chatbots per minute
async def create_chatbot(...):
...
4. ⚠️ CORS ALLOWS ALL ORIGINS
Location: Lines 21-27
Same as other services
Security Best Practices Implemented¶
✅ Soft Delete: Uses isDeleted flag instead of hard delete
✅ Upsert Pattern: Prevents duplicate records in selection_history
✅ Timestamp Tracking: All projects have created_at
Deployment¶
Docker Configuration¶
Dockerfile:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ .
EXPOSE 8003
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003"]
Docker Compose¶
create-chatbot-service:
build: ./create-chatbot-service
container_name: create-chatbot-service
ports:
- "8003:8003"
networks:
- app-network
labels:
- "logging=loki"
- "app=machine-agent-app"
environment:
- MONGO_URI=${MONGO_URI}
- MONGO_DB_NAME=Machine_agent_demo
- DEFAULT_DATABASE=milvus
# Azure Email (should be added)
- AZURE_EMAIL_ENDPOINT=${AZURE_EMAIL_ENDPOINT}
- AZURE_EMAIL_ACCESS_KEY=${AZURE_EMAIL_ACCESS_KEY}
# DataDog
- DD_SERVICE=create-chatbot-service
- DD_ENV=production
restart: always
Requirements.txt¶
fastapi>=0.95.0
uvicorn[standard]>=0.22.0
pymongo>=4.3.3
python-multipart>=0.0.6
python-dotenv>=1.0.0
azure-communication-email>=1.0.0
ddtrace>=1.19.0
Performance Metrics¶
| Operation | Latency | Notes |
|---|---|---|
| Create chatbot (no email) | 50-100ms | 2 DB inserts |
| Create chatbot (with warning email) | 2-5 seconds | + Azure email |
| Get chatbots | 20-50ms | Single DB query |
| Check status | 10-30ms | Single DB lookup |
| State checking | 20-40ms | 1-2 DB queries |
Related Documentation¶
- Gateway Service - Routes to this service
- User Service - Creates users and features
- Selection Chatbot Service - Updates selection_history
- System Architecture
Recommendations & Next Steps¶
Critical¶
- ⚠️ IMPLEMENT QUOTA CHECKING - Functions exist but not called!
- ⚠️ Move Email Credentials to Environment Variables
- ⚠️ Add Rate Limiting
Improvements¶
- Improve Email Templates - Use HTML, add branding
- Add Quota Check in
/v2/create-chatbot- Currently missing! - Unified State Check - Combine 3D/Text/Voice into one function
- Add Project Deletion - Currently only soft delete
Code Quality¶
- Remove Duplicate Endpoint -
/v2/create-chatbotvs/v2/create-chatbot1 - Add Type Hints - Improve code documentation
- Add Unit Tests - Test quota logic, state checking
- Consistent Naming -
chatbotvsprojectterminology
Last Updated: 2025-12-26
Code Version: create-chatbot-service/src/main.py (521 lines)
Total Endpoints: 11
Review Cycle: Monthly
"Every great chatbot starts with a project ID."