A powerful, embeddable AI customer support solution that can be integrated into any website with a single JavaScript snippet. Built using DigitalOcean's Gradient AI platform for AI agent capabilities and Memori for persistent conversation memory.
- Architecture Guide - Complete system architecture, data flows, and component responsibilities
- Quick Reference - Fast API reference for common operations
- 🤖 AI-Powered Support: Uses DigitalOcean's Gradient AI platform for contextual understanding
- 🧠 Persistent Memory: Domain-specific conversation memory with Memori integration
- 🕷️ Knowledge Base Management: File uploads, text content, and URL scraping for context-aware responses
- 💾 Persistent Storage: Agents and knowledge bases stored in PostgreSQL with automatic synchronization
- 🚀 Easy Integration: Single JavaScript snippet for any website
- 🐳 Docker Ready: Complete containerized setup with docker-compose
- 🔒 Domain-Based Security: Per-domain authentication and memory isolation
- 🎨 Customizable Widget: Configurable appearance and behavior
- ⚡ Non-Blocking Deployment: Background agent deployment with instant user feedback
For detailed architecture documentation, see ARCHITECTURE.md.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Website │ │ FastAPI │ │ PostgreSQL │
│ + Widget.js │◄──►│ Backend │◄──►│ (Persistence) │
│ │ │ │ │ • Agents │
└─────────────────┘ └──────────────────┘ │ • KBs │
│ │ • Sessions │
│ │ • Domains │
┌───────────┼───────────┐ └─────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌────────────┐ ┌──────────────┐
│ DigitalOcean │ │ Memori │ │ Knowledge │
│ Gradient AI │ │ API │ │ Bases (DO) │
│ (Agents) │ │ (per-domain)│ │ │
└──────────────┘ └────────────┘ └──────────────┘
Key Components:
- DigitalOcean Client (
digitalocean_client.py) - Agent & KB management, access key creation - Memori Client (
memori_client.py) - Domain-specific conversation memory - Knowledge Uploader (
knowledge_upload.py) - File processing and knowledge base population - Domain-Based Authentication - Per-domain API keys and memory isolation
- Docker and Docker Compose
- DigitalOcean API token with Gradient AI access
- Python 3.11+ (for local development)
git clone https://github.com/Boburmirzo/customer-support-agent-memory.git
cd customer-support-agent-memori
# Update your DigitalOcean credentials in .env file
cp .env.example .env
# Edit .env and add your DIGITALOCEAN_TOKEN and other DigitalOcean variables# Build and start all services
docker-compose up --build
# Or run in detached mode
docker-compose up -d --buildThe API will be available at http://localhost:8000
Open http://localhost:8000/static/demo.html to see the widget in action.
pip install -r requirements.txt# Install PostgreSQL
# On macOS with Homebrew:
brew install postgresql
# Start PostgreSQL
brew services start postgresql
# Create database and user
createdb customer_support
psql customer_support < init.sql
# For existing installations, run the migration:
psql -h localhost -U do_user -d customer_support -f migrate_db.sql# Copy and edit environment variables
cp .env.example .env
# Edit .env with your database and API settingsuvicorn main:app --reload --host 0.0.0.0 --port 8000Add this to your website's HTML:
<!-- Include the widget script -->
<script src="http://localhost:8000/static/widget.js" data-domain-id="your-domain-id"></script>For complete API documentation, see QUICK_REFERENCE.md.
POST /register-domain
Content-Type: application/json
Authorization: Bearer YOUR_ADMIN_API_KEY
{
"domain_name": "example.com",
"memori_api_key": "your-memori-key" (optional)
}
Response:
{
"domain_id": "uuid-here",
"api_key": "generated-api-key",
"message": "Domain registered successfully"
}POST /session
Content-Type: application/json
X-Domain-ID: your-domain-id
{
"user_id": "user123",
"website_url": "https://example.com" (optional)
}POST /ask
Content-Type: application/json
X-Domain-ID: your-domain-id
{
"question": "How do I reset my password?",
"session_id": "uuid-here",
"user_id": "user123"
}POST /knowledge/upload/file
Content-Type: multipart/form-data
X-Domain-ID: your-domain-id
Form data:
- website_url: "https://example.com"
- file: [binary file data]
- chunk_size: 1000 (optional)
- use_semantic: false (optional)
- custom_name: "Custom Document Name" (optional)
Supported file types:
- PDF (.pdf) - Extracts text from PDF documents
- Text (.txt) - Plain text files
- Markdown (.md) - Markdown documents with formatting
- JSON (.json) - Structured JSON data
- CSV (.csv) - Tabular data from CSV filesPOST /knowledge/upload/text
Content-Type: application/json
X-Domain-ID: your-domain-id
{
"website_url": "https://example.com",
"text_content": "Your plain text content here...",
"document_name": "My Document",
"chunk_size": 1000,
"use_semantic": false
}POST /knowledge/upload/url
Content-Type: application/json
X-Domain-ID: your-domain-id
{
"website_url": "https://example.com",
"url_to_scrape": "https://docs.example.com",
"max_depth": 2,
"max_links": 20,
"chunk_size": 1000
}GET /knowledge/supported-types
X-Domain-ID: your-domain-id
Response:
{
"supported_types": [".pdf", ".txt", ".md", ".json", ".csv"],
"descriptions": {
".pdf": "PDF documents",
".txt": "Plain text files",
".md": "Markdown documents",
".json": "JSON data files",
".csv": "CSV data files"
},
"additional_sources": ["url", "text"]
}POST /scrape
Content-Type: application/json
X-Domain-ID: your-domain-id
{
"website_url": "https://example.com",
"max_depth": 2,
"max_links": 20
}Note: Widget no longer auto-scrapes. Use this endpoint manually to populate knowledge base.
| Variable | Description | Default |
|---|---|---|
DIGITALOCEAN_TOKEN |
DigitalOcean API token (required) | - |
DIGITALOCEAN_AGENT_NAME |
Agent name prefix | customer-support |
DIGITALOCEAN_KNOWLEDGE_BASE_NAME |
KB name prefix | website-kb |
DIGITALOCEAN_AGENT_INSTRUCTIONS |
Agent instructions | Custom instructions |
DIGITALOCEAN_MODEL |
AI model to use | deepseek-ai/DeepSeek-V3 |
MEMORI_BASE_URL |
Memori API endpoint | https://memori-api-89r6e.ondigitalocean.app |
MEMORI_API_KEY |
Default Memori API key (optional) | - |
POSTGRES_HOST |
PostgreSQL host | localhost |
POSTGRES_PORT |
PostgreSQL port | 5432 |
POSTGRES_DB |
Database name | customer_support |
POSTGRES_USER |
Database user | do_user |
POSTGRES_PASSWORD |
Database password | do_user_password |
ADMIN_API_KEY |
Admin API key for domain registration | test_api_key_123 |
The system automatically creates the following tables:
registered_domains- Domain registration with API keys and Memori configurationuser_sessions- Manages user sessions with status trackingagents- Stores DigitalOcean agent metadata and access keys (domain-based)knowledge_bases- Tracks knowledge base UUIDs and website associationsconversation_history- Stores chat history per session
-- Registered domains with per-domain configuration
CREATE TABLE registered_domains (
id UUID PRIMARY KEY,
domain_name TEXT UNIQUE NOT NULL, -- Base domain (e.g., "example.com")
api_key TEXT UNIQUE NOT NULL, -- Domain-specific API key
memori_api_key TEXT, -- Optional Memori API key
created_at TIMESTAMP DEFAULT NOW()
);
-- Agents table stores DigitalOcean agent information
CREATE TABLE agents (
website_key TEXT PRIMARY KEY, -- Hash of domain_name
agent_uuid UUID NOT NULL,
agent_url TEXT NOT NULL,
agent_access_key TEXT, -- Fresh API key (32 chars)
knowledge_base_uuids TEXT[],
website_url TEXT, -- Normalized as https://{domain_name}
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Knowledge Bases table stores KB metadata
CREATE TABLE knowledge_bases (
website_key TEXT PRIMARY KEY,
kb_uuid UUID NOT NULL,
website_url TEXT NOT NULL,
kb_name TEXT,
created_at TIMESTAMP DEFAULT NOW()
);Check stored resources:
-- View all registered domains
SELECT id, domain_name, created_at
FROM registered_domains
ORDER BY created_at DESC;
-- View all agents with their domains
SELECT a.website_key, a.agent_uuid, a.website_url, a.agent_access_key IS NOT NULL as has_key, a.created_at
FROM agents a
ORDER BY a.created_at DESC;
-- View all knowledge bases
SELECT website_key, kb_uuid, website_url, kb_name
FROM knowledge_bases
ORDER BY created_at DESC;
-- Count resources
SELECT
(SELECT COUNT(*) FROM registered_domains) as total_domains,
(SELECT COUNT(*) FROM agents) as total_agents,
(SELECT COUNT(*) FROM knowledge_bases) as total_kbs,
(SELECT COUNT(*) FROM user_sessions WHERE status = 'active') as active_sessions;
-- Check agent access keys
SELECT website_key,
agent_uuid,
LENGTH(agent_access_key) as key_length,
agent_access_key IS NOT NULL as has_key
FROM agents;- API Keys: Store DigitalOcean token securely
- CORS: Configure allowed origins properly
- Rate Limiting: Implement rate limiting for API endpoints
- SSL: Use HTTPS in production
- Database: Secure PostgreSQL with proper authentication
- Agent Access Keys: Stored securely in database, encrypted at rest
- Database: Use managed PostgreSQL service (DigitalOcean's Fully Managed PostgreSQL)
- API: Deploy behind load balancer
- Caching: Implement Redis for session caching
- CDN: Serve widget.js from CDN
Monitor these metrics:
- API response times
- Database performance (check
agentsandknowledge_basestables) - Session creation rate
- DigitalOcean API usage and quotas
- Error rates
- Memory cache hit/miss rates
For detailed troubleshooting, see ARCHITECTURE.md - Troubleshooting Section.
-
Widget not appearing
- Check console for JavaScript errors
- Verify API URL is correct
- Ensure CORS is configured properly
- Verify X-Domain-ID header is set correctly
-
401 Unauthorized errors
- Fixed: Access keys now created fresh via POST /agents/{uuid}/api_keys
- Verify domain is registered: Check
registered_domainstable - Ensure X-Domain-ID header matches registered domain ID
- Check agent has valid access key:
SELECT agent_access_key FROM agents WHERE website_key = '...'
-
Duplicate agents created
- Fixed: Now uses domain_name from registered_domains for consistent website_key
- Single agent per registered domain regardless of URL variations (www vs non-www)
- Query:
SELECT * FROM agents WHERE website_url LIKE '%example.com%'
-
Knowledge base 404 errors
- Fixed: KBs now attached after agent deployment completes
- Background polling waits for STATUS_RUNNING before attaching KBs
- Check agent status: Look for "Background polling completed" in logs
-
Database connection errors
- Verify PostgreSQL is running
- Check DATABASE_URL format
- Verify user permissions (do_user)
- Ensure all required tables exist (run init.sql if needed)
-
DigitalOcean API errors
- Verify DIGITALOCEAN_TOKEN is valid
- Check account access to Gradient AI
- Monitor API rate limits
- Check agent and KB creation logs for specific error messages
-
Memori integration issues
- Fixed: Now uses domain-specific API keys from registered_domains
- Verify memori_api_key is set for domain
- Check Memori API endpoint:
MEMORI_BASE_URL - Monitor logs for "Memori API success" messages
-
Widget not auto-scraping
- By design: Auto-scraping removed for performance
- Use manual
/scrapeendpoint to populate knowledge base - Or upload files via
/knowledge/upload/*endpoints
├── main.py # FastAPI application with all endpoints
├── digitalocean_client.py # DigitalOcean Gradient AI client
│ # - Agent creation & management
│ # - Knowledge base operations
│ # - Access key creation (POST /api_keys)
├── memori_client.py # Memori API client (domain-specific memory)
│ # - MemoriClient class
│ # - Async chat() and get_context() methods
├── knowledge_upload.py # Knowledge base file upload handler
├── memori_tool.py # Memori tool integration (legacy)
├── auth.py # Authentication utilities
├── requirements.txt # Python dependencies
├── docker-compose.yml # Docker services configuration
├── Dockerfile # API container definition
├── init.sql # Database initialization schema
├── Procfile # Heroku deployment configuration
├── .env # Environment variables (not in repo)
├── ARCHITECTURE.md # 📚 Complete system architecture
├── QUICK_REFERENCE.md # 📘 API quick reference guide
├── README.md # This file
├── static/
│ ├── widget.js # Embeddable widget (no auto-scrape)
│ ├── demo.html # Integration demo page
│ └── knowledge_upload_demo.html # KB upload demo
└── __pycache__/ # Python bytecode cache
Key Files:
- main.py: Core FastAPI app with domain registration, session management, chat, and knowledge endpoints
- digitalocean_client.py: Handles all DigitalOcean API interactions including agent/KB creation and access key management
- memori_client.py: Dedicated module for Memori API integration with per-domain memory isolation
- knowledge_upload.py: Processes file uploads (PDF, TXT, MD, JSON, CSV) and URL scraping
- widget.js: Client-side embeddable widget (updated to remove auto-scraping)
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Create an issue in the repository
- Check the troubleshooting section
- Review the API documentation at
/docs