From 586059b14f7e3f7fac018c57bb4ff5c11ba52ea4 Mon Sep 17 00:00:00 2001 From: thulasiraman S Date: Fri, 28 Aug 2026 02:46:38 +0530 Subject: [PATCH] WeLe Agentic AI with Docker deployment --- .gitignore | 28 ++++++++++++---------- docker-compose.yml | 5 ++-- src/data/models/index.js | 52 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 src/data/models/index.js diff --git a/.gitignore b/.gitignore index a3430cd..68e0ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,15 +30,15 @@ pnpm-debug.log* # ── Python (voice service) ────────────────────────────────────────────────── voice-service/.venv/ .venv/ -venv/ -env/ +/venv/ +/env/ __pycache__/ *.py[cod] *$py.class *.egg-info/ .eggs/ -build/ -dist/ +/build/ +/dist/ .pytest_cache/ .mypy_cache/ .ruff_cache/ @@ -48,9 +48,13 @@ dist/ # The AI4Bharat and Whisper checkpoints are several GB and are re-downloaded # from HuggingFace on first run. They are gated, so committing them would also # redistribute models the licence does not allow us to redistribute. -.cache/ -huggingface/ -models/ +# +# Leading slashes matter: an unanchored `models/` also matches +# `src/data/models/` — the CRM read-models — which silently kept them out of +# the repo and made the container crash with ERR_MODULE_NOT_FOUND. +/.cache/ +/huggingface/ +/models/ *.onnx *.safetensors *.ckpt @@ -64,7 +68,7 @@ models/ # contain customer data. storage/artifacts/* !storage/artifacts/.gitkeep -logs/ +/logs/ *.log # Scratch audio from voice debugging @@ -86,7 +90,7 @@ desktop.ini # ── Local tooling ─────────────────────────────────────────────────────────── # Per-machine Claude Code settings; the shared .claude/settings.json is tracked. .claude/settings.local.json -coverage/ -.nyc_output/ -tmp/ -temp/ +/coverage/ +/.nyc_output/ +/tmp/ +/temp/ diff --git a/docker-compose.yml b/docker-compose.yml index 3f81949..80580f5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,12 +33,13 @@ services: - REDIS_ENABLED=true - REDIS_HOST=redis - REDIS_PORT=6379 - - REDIS_PREFIX=agentic: + # Quoted: a trailing colon makes YAML parse this as a map, not a string. + - "REDIS_PREFIX=agentic:" # Writes go through the CRM's own REST routes so its lead scoring, # socket events and audit trail still fire. Reached over the shared # network, so this never leaves the host. - - CRM_API_BASE=http://whatsapp-api:3000 + - "CRM_API_BASE=http://whatsapp-api:3000" # Must match the CRM's JWT_SECRET — that is how a CRM login is accepted # here with the same role and permissions. Sourced from .env so the diff --git a/src/data/models/index.js b/src/data/models/index.js new file mode 100644 index 0000000..09448e6 --- /dev/null +++ b/src/data/models/index.js @@ -0,0 +1,52 @@ +// ============================================ +// Read-models over the live CRM collections. +// +// Deliberately schema-less (`strict: false`): this service only READS from +// these collections, and the CRM owns the real schemas. Re-declaring 26 full +// schemas here would create two definitions of the same truth that silently +// drift apart the first time the CRM adds a field. Collection names are +// pinned explicitly — they were verified against the live database rather +// than inferred from Mongoose's pluralisation rules. +// ============================================ +import mongoose from 'mongoose'; + +const loose = (collection) => + new mongoose.Schema({}, { strict: false, collection, versionKey: false }); + +const model = (name, collection) => + mongoose.models[name] || mongoose.model(name, loose(collection)); + +export const Lead = model('Lead', 'leads'); +export const Conversation = model('Conversation', 'conversations'); +export const LeadStage = model('LeadStage', 'leadstages'); +export const AnalyticsEvent = model('AnalyticsEvent', 'analyticsevents'); +export const SummaryNote = model('SummaryNote', 'summarynotes'); +export const WorkshopStage = model('WorkshopStage', 'workshopstages'); +export const EnrollmentLog = model('EnrollmentLog', 'enrollmentlogs'); +export const CallLog = model('CallLog', 'calllogs'); +export const CallSession = model('CallSession', 'callsessions'); +export const Campaign = model('Campaign', 'campaigns'); +export const FollowUp = model('FollowUp', 'followups'); +export const DSRReport = model('DSRReport', 'dsrreports'); +export const User = model('User', 'users'); +export const Role = model('Role', 'roles'); +export const Course = model('Course', 'courses'); +export const Trainer = model('Trainer', 'trainers'); +export const Tag = model('Tag', 'tags'); +export const StageConfig = model('StageConfig', 'stageconfigs'); +export const WhatsAppLead = model('WhatsAppLead', 'whatsappleads'); +export const CommentLead = model('CommentLead', 'commentleads'); +export const InstaAdLead = model('InstaAdLead', 'instaadleads'); +export const ScheduledEvent = model('ScheduledEvent', 'scheduledevents'); +export const EmailTemplate = model('EmailTemplate', 'emailtemplates'); + +// ── Paid acquisition / attribution ── +export const CapiEvent = model('CapiEvent', 'capievents'); +export const WhatsAppTemplLead = model('WhatsAppTemplLead', 'whatsapptemplleads'); + +export const COLLECTIONS = { + Lead, Conversation, LeadStage, AnalyticsEvent, SummaryNote, WorkshopStage, + EnrollmentLog, CallLog, CallSession, Campaign, FollowUp, DSRReport, User, + Role, Course, Trainer, Tag, StageConfig, WhatsAppLead, CommentLead, + InstaAdLead, ScheduledEvent, EmailTemplate, CapiEvent, WhatsAppTemplLead, +};