const mongoose = require('mongoose'); const messageSchema = new mongoose.Schema( { role: { type: String, enum: ['user', 'assistant'], required: true }, text: { type: String, required: true }, ts: { type: Date, default: Date.now }, modelId: { type: String, default: null }, inputTokens: { type: Number, default: 0 }, outputTokens: { type: Number, default: 0 }, }, { _id: false }, ); // Two-stage concierge pipeline turn log. Kept in sync with the turn log shape // produced by utils/conciergePipelineLog.js. Stores previews only (no raw PII). const pipelineCall1Schema = new mongoose.Schema( { skipped: { type: Boolean, default: false }, path: { type: String, default: null }, modelId: { type: String, default: null }, intent: { type: String, default: null }, needsSecondCall: { type: Boolean, default: null }, inputTokens: { type: Number, default: 0 }, outputTokens: { type: Number, default: 0 }, latencyMs: { type: Number, default: 0 }, rawPreview: { type: String, default: null }, }, { _id: false }, ); const pipelineHandlerSchema = new mongoose.Schema( { name: { type: String, default: null }, skippedCall2: { type: Boolean, default: false }, cardCount: { type: Number, default: 0 }, cardTypes: { type: [String], default: [] }, latencyMs: { type: Number, default: 0 }, }, { _id: false }, ); const pipelineCall2Schema = new mongoose.Schema( { skipped: { type: Boolean, default: true }, reason: { type: String, default: null }, modelId: { type: String, default: null }, inputTokens: { type: Number, default: 0 }, outputTokens: { type: Number, default: 0 }, latencyMs: { type: Number, default: 0 }, retrievalSources: { type: Number, default: 0 }, rawPreview: { type: String, default: null }, }, { _id: false }, ); const pipelineTotalsSchema = new mongoose.Schema( { inputTokens: { type: Number, default: 0 }, outputTokens: { type: Number, default: 0 }, totalTokens: { type: Number, default: 0 }, latencyMs: { type: Number, default: 0 }, }, { _id: false }, ); const pipelineTurnSchema = new mongoose.Schema( { ts: { type: Date, default: Date.now }, sessionId: { type: String, default: null }, botId: { type: String, default: null }, userMessagePreview: { type: String, default: null }, call1: { type: pipelineCall1Schema, default: () => ({}) }, handler: { type: pipelineHandlerSchema, default: () => ({}) }, call2: { type: pipelineCall2Schema, default: () => ({}) }, totals: { type: pipelineTotalsSchema, default: () => ({}) }, replyPreview: { type: String, default: null }, }, { _id: false }, ); const webChatSessionSchema = new mongoose.Schema( { botId: { type: mongoose.Schema.Types.ObjectId, ref: 'ConciergeBot', required: true, index: true }, eventId: { type: mongoose.Schema.Types.ObjectId, ref: 'Event', required: true, index: true }, organization: { type: mongoose.Schema.Types.ObjectId, ref: 'Organization', required: true, index: true }, sessionId: { type: String, required: true, index: true }, participant: { type: mongoose.Schema.Types.ObjectId, ref: 'Participant', default: null, index: true }, awaitingPersonalEmail: { type: Boolean, default: false }, /** Last successfully routed information intent (for affirmative "yes" continuations). */ lastIntent: { type: String, default: null }, identityVerifiedAt: { type: Date, default: null }, identityMethod: { type: String, enum: ['email'], default: null }, endedAt: { type: Date, default: null, index: true }, history: { type: [messageSchema], default: [] }, pipelineLog: { type: [pipelineTurnSchema], default: [] }, lastSeenAt: { type: Date, default: Date.now, index: true }, inputTokensTotal: { type: Number, default: 0 }, outputTokensTotal: { type: Number, default: 0 }, }, { timestamps: true }, ); webChatSessionSchema.index({ botId: 1, sessionId: 1 }, { unique: true }); module.exports = mongoose.model('WebChatSession', webChatSessionSchema);