myavana-bot-test-core 2.0.6 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
-- Migration 003: Sienna Naturals Enhancements and Optimizations
|
|
2
|
+
-- Extends the database schema with Sienna Naturals specific tables and optimizations
|
|
3
|
+
-- Run this after 001_add_analytics_tables.sql and 002_add_advanced_features_tables.sql
|
|
4
|
+
|
|
5
|
+
-- Sienna Naturals specific brand tracking
|
|
6
|
+
CREATE TABLE IF NOT EXISTS brand_interactions (
|
|
7
|
+
id SERIAL PRIMARY KEY,
|
|
8
|
+
user_id VARCHAR(255) NOT NULL,
|
|
9
|
+
conversation_id VARCHAR(255),
|
|
10
|
+
brand_name VARCHAR(100) NOT NULL, -- 'sienna_naturals', 'myavana', etc.
|
|
11
|
+
interaction_type VARCHAR(100), -- product_inquiry, store_locator, discount_request
|
|
12
|
+
interaction_data JSONB,
|
|
13
|
+
user_satisfaction INTEGER,
|
|
14
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- Store locator requests (Sienna Naturals specific)
|
|
18
|
+
CREATE TABLE IF NOT EXISTS store_locator_requests (
|
|
19
|
+
id SERIAL PRIMARY KEY,
|
|
20
|
+
user_id VARCHAR(255) NOT NULL,
|
|
21
|
+
location_query VARCHAR(255),
|
|
22
|
+
user_location JSONB, -- {city, state, zip, coordinates if available}
|
|
23
|
+
stores_found INTEGER DEFAULT 0,
|
|
24
|
+
stores_data JSONB, -- array of store information returned
|
|
25
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
-- Product availability tracking by brand
|
|
29
|
+
CREATE TABLE IF NOT EXISTS product_availability_tracking (
|
|
30
|
+
id SERIAL PRIMARY KEY,
|
|
31
|
+
brand_name VARCHAR(100) NOT NULL,
|
|
32
|
+
product_name VARCHAR(255) NOT NULL,
|
|
33
|
+
availability_status VARCHAR(50) DEFAULT 'available', -- available, out_of_stock, discontinued
|
|
34
|
+
inventory_level VARCHAR(50), -- high, medium, low
|
|
35
|
+
last_checked TIMESTAMP DEFAULT NOW(),
|
|
36
|
+
notifications_sent INTEGER DEFAULT 0,
|
|
37
|
+
UNIQUE(brand_name, product_name)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
-- Enhanced circuit breaker states for AI models
|
|
41
|
+
CREATE TABLE IF NOT EXISTS circuit_breaker_states (
|
|
42
|
+
id SERIAL PRIMARY KEY,
|
|
43
|
+
model_name VARCHAR(100) NOT NULL UNIQUE,
|
|
44
|
+
state VARCHAR(20) DEFAULT 'closed', -- closed, open, half_open
|
|
45
|
+
failure_count INTEGER DEFAULT 0,
|
|
46
|
+
last_failure_time TIMESTAMP,
|
|
47
|
+
last_success_time TIMESTAMP,
|
|
48
|
+
threshold_failures INTEGER DEFAULT 5,
|
|
49
|
+
timeout_duration INTERVAL DEFAULT '5 minutes',
|
|
50
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
-- Response caching with brand-specific keys
|
|
54
|
+
CREATE TABLE IF NOT EXISTS response_cache (
|
|
55
|
+
id SERIAL PRIMARY KEY,
|
|
56
|
+
cache_key VARCHAR(512) NOT NULL UNIQUE, -- hash of query + user context + brand
|
|
57
|
+
brand_name VARCHAR(100),
|
|
58
|
+
cached_response JSONB NOT NULL,
|
|
59
|
+
user_context_hash VARCHAR(64), -- hash of user profile for personalization
|
|
60
|
+
query_hash VARCHAR(64), -- hash of the query
|
|
61
|
+
context_analysis JSONB, -- context analysis data
|
|
62
|
+
hit_count INTEGER DEFAULT 0,
|
|
63
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
64
|
+
expires_at TIMESTAMP DEFAULT NOW() + INTERVAL '24 hours'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
-- Smart prompt analytics
|
|
68
|
+
CREATE TABLE IF NOT EXISTS smart_prompt_analytics (
|
|
69
|
+
id SERIAL PRIMARY KEY,
|
|
70
|
+
user_id VARCHAR(255) NOT NULL,
|
|
71
|
+
conversation_id VARCHAR(255),
|
|
72
|
+
brand_name VARCHAR(100),
|
|
73
|
+
query_analysis JSONB, -- topics, intent, complexity
|
|
74
|
+
prompt_strategy VARCHAR(100), -- educational, sales, support, etc.
|
|
75
|
+
context_elements_used JSONB, -- faqs, products, previous_conversations
|
|
76
|
+
prompt_length INTEGER,
|
|
77
|
+
response_quality_score FLOAT, -- 0-1 based on user engagement
|
|
78
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
-- User engagement patterns
|
|
82
|
+
CREATE TABLE IF NOT EXISTS user_engagement_patterns (
|
|
83
|
+
id SERIAL PRIMARY KEY,
|
|
84
|
+
user_id VARCHAR(255) NOT NULL UNIQUE,
|
|
85
|
+
brand_name VARCHAR(100),
|
|
86
|
+
total_interactions INTEGER DEFAULT 0,
|
|
87
|
+
avg_session_duration INTERVAL,
|
|
88
|
+
preferred_interaction_time TIME,
|
|
89
|
+
preferred_day_of_week INTEGER, -- 0-6, 0=Sunday
|
|
90
|
+
response_style_preference VARCHAR(100), -- detailed, concise, visual, etc.
|
|
91
|
+
topic_preferences JSONB, -- array of preferred topics
|
|
92
|
+
engagement_score FLOAT DEFAULT 0, -- 0-1 based on overall engagement
|
|
93
|
+
last_interaction TIMESTAMP,
|
|
94
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
-- Model performance benchmarks
|
|
98
|
+
CREATE TABLE IF NOT EXISTS model_performance_benchmarks (
|
|
99
|
+
id SERIAL PRIMARY KEY,
|
|
100
|
+
model_name VARCHAR(100) NOT NULL,
|
|
101
|
+
brand_name VARCHAR(100),
|
|
102
|
+
benchmark_type VARCHAR(100), -- response_time, accuracy, user_satisfaction
|
|
103
|
+
benchmark_value FLOAT,
|
|
104
|
+
sample_size INTEGER,
|
|
105
|
+
measurement_date DATE DEFAULT CURRENT_DATE,
|
|
106
|
+
measurement_context JSONB, -- conditions during measurement
|
|
107
|
+
UNIQUE(model_name, brand_name, benchmark_type, measurement_date)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
-- Advanced indexes for performance
|
|
111
|
+
CREATE INDEX IF NOT EXISTS idx_brand_interactions_user_brand ON brand_interactions(user_id, brand_name, created_at);
|
|
112
|
+
CREATE INDEX IF NOT EXISTS idx_store_locator_location ON store_locator_requests(user_location);
|
|
113
|
+
CREATE INDEX IF NOT EXISTS idx_response_cache_key ON response_cache(cache_key, expires_at) WHERE expires_at > NOW();
|
|
114
|
+
CREATE INDEX IF NOT EXISTS idx_response_cache_brand_context ON response_cache(brand_name, user_context_hash) WHERE expires_at > NOW();
|
|
115
|
+
CREATE INDEX IF NOT EXISTS idx_smart_prompt_user_brand ON smart_prompt_analytics(user_id, brand_name, created_at);
|
|
116
|
+
CREATE INDEX IF NOT EXISTS idx_user_engagement_brand ON user_engagement_patterns(brand_name, engagement_score DESC);
|
|
117
|
+
CREATE INDEX IF NOT EXISTS idx_circuit_breaker_model ON circuit_breaker_states(model_name, state);
|
|
118
|
+
|
|
119
|
+
-- Composite indexes for common query patterns
|
|
120
|
+
CREATE INDEX IF NOT EXISTS idx_conversation_metrics_composite ON conversation_metrics(user_id, ai_model_used, created_at);
|
|
121
|
+
CREATE INDEX IF NOT EXISTS idx_user_journey_composite ON user_journey_events(user_id, event_type, hair_journey_stage, created_at);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_error_logs_composite ON error_logs(model_name, error_type, recovery_successful, created_at);
|
|
123
|
+
|
|
124
|
+
-- Partitioning preparation (for high-volume tables)
|
|
125
|
+
-- Note: These are preparation steps for future partitioning
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_conversation_metrics_created_monthly ON conversation_metrics(DATE_TRUNC('month', created_at));
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_user_journey_events_created_monthly ON user_journey_events(DATE_TRUNC('month', created_at));
|
|
128
|
+
CREATE INDEX IF NOT EXISTS idx_error_logs_created_monthly ON error_logs(DATE_TRUNC('month', created_at));
|
|
129
|
+
|
|
130
|
+
-- Views for Sienna Naturals specific analytics
|
|
131
|
+
CREATE OR REPLACE VIEW sienna_naturals_user_stats AS
|
|
132
|
+
SELECT
|
|
133
|
+
DATE(created_at) as date,
|
|
134
|
+
COUNT(DISTINCT user_id) as unique_users,
|
|
135
|
+
COUNT(*) as total_interactions,
|
|
136
|
+
COUNT(CASE WHEN interaction_type = 'product_inquiry' THEN 1 END) as product_inquiries,
|
|
137
|
+
COUNT(CASE WHEN interaction_type = 'store_locator' THEN 1 END) as store_locator_requests,
|
|
138
|
+
COUNT(CASE WHEN interaction_type = 'discount_request' THEN 1 END) as discount_requests,
|
|
139
|
+
AVG(user_satisfaction) as avg_satisfaction
|
|
140
|
+
FROM brand_interactions
|
|
141
|
+
WHERE brand_name = 'sienna_naturals'
|
|
142
|
+
GROUP BY DATE(created_at)
|
|
143
|
+
ORDER BY date DESC;
|
|
144
|
+
|
|
145
|
+
CREATE OR REPLACE VIEW brand_performance_comparison AS
|
|
146
|
+
SELECT
|
|
147
|
+
brand_name,
|
|
148
|
+
COUNT(DISTINCT user_id) as unique_users,
|
|
149
|
+
COUNT(*) as total_interactions,
|
|
150
|
+
AVG(user_satisfaction) as avg_satisfaction,
|
|
151
|
+
COUNT(CASE WHEN user_satisfaction >= 4 THEN 1 END) as satisfied_interactions,
|
|
152
|
+
ROUND(
|
|
153
|
+
COUNT(CASE WHEN user_satisfaction >= 4 THEN 1 END)::float /
|
|
154
|
+
COUNT(CASE WHEN user_satisfaction IS NOT NULL THEN 1 END) * 100, 2
|
|
155
|
+
) as satisfaction_rate
|
|
156
|
+
FROM brand_interactions
|
|
157
|
+
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
|
|
158
|
+
GROUP BY brand_name
|
|
159
|
+
ORDER BY satisfaction_rate DESC;
|
|
160
|
+
|
|
161
|
+
CREATE OR REPLACE VIEW cache_performance_stats AS
|
|
162
|
+
SELECT
|
|
163
|
+
brand_name,
|
|
164
|
+
COUNT(*) as total_cached_responses,
|
|
165
|
+
SUM(hit_count) as total_cache_hits,
|
|
166
|
+
COUNT(CASE WHEN expires_at > NOW() THEN 1 END) as active_cache_entries,
|
|
167
|
+
AVG(hit_count) as avg_hits_per_entry,
|
|
168
|
+
ROUND(
|
|
169
|
+
COUNT(CASE WHEN hit_count > 0 THEN 1 END)::float /
|
|
170
|
+
COUNT(*) * 100, 2
|
|
171
|
+
) as cache_utilization_rate
|
|
172
|
+
FROM response_cache
|
|
173
|
+
GROUP BY brand_name
|
|
174
|
+
ORDER BY cache_utilization_rate DESC;
|
|
175
|
+
|
|
176
|
+
CREATE OR REPLACE VIEW model_circuit_breaker_status AS
|
|
177
|
+
SELECT
|
|
178
|
+
model_name,
|
|
179
|
+
state,
|
|
180
|
+
failure_count,
|
|
181
|
+
last_failure_time,
|
|
182
|
+
last_success_time,
|
|
183
|
+
CASE
|
|
184
|
+
WHEN state = 'open' AND last_failure_time + timeout_duration < NOW() THEN 'ready_for_half_open'
|
|
185
|
+
WHEN state = 'closed' AND failure_count >= threshold_failures THEN 'approaching_threshold'
|
|
186
|
+
ELSE 'normal'
|
|
187
|
+
END as status_indicator
|
|
188
|
+
FROM circuit_breaker_states
|
|
189
|
+
ORDER BY
|
|
190
|
+
CASE state
|
|
191
|
+
WHEN 'open' THEN 1
|
|
192
|
+
WHEN 'half_open' THEN 2
|
|
193
|
+
WHEN 'closed' THEN 3
|
|
194
|
+
END,
|
|
195
|
+
failure_count DESC;
|
|
196
|
+
|
|
197
|
+
-- Functions for automatic maintenance
|
|
198
|
+
CREATE OR REPLACE FUNCTION cleanup_expired_cache()
|
|
199
|
+
RETURNS INTEGER AS $$
|
|
200
|
+
DECLARE
|
|
201
|
+
deleted_count INTEGER;
|
|
202
|
+
BEGIN
|
|
203
|
+
DELETE FROM response_cache WHERE expires_at < NOW();
|
|
204
|
+
GET DIAGNOSTICS deleted_count = ROW_COUNT;
|
|
205
|
+
RETURN deleted_count;
|
|
206
|
+
END;
|
|
207
|
+
$$ LANGUAGE plpgsql;
|
|
208
|
+
|
|
209
|
+
CREATE OR REPLACE FUNCTION update_engagement_patterns()
|
|
210
|
+
RETURNS VOID AS $$
|
|
211
|
+
BEGIN
|
|
212
|
+
-- Update engagement scores based on recent activity
|
|
213
|
+
UPDATE user_engagement_patterns
|
|
214
|
+
SET
|
|
215
|
+
engagement_score = COALESCE((
|
|
216
|
+
SELECT
|
|
217
|
+
CASE
|
|
218
|
+
WHEN AVG(user_satisfaction) >= 4.5 THEN 0.9
|
|
219
|
+
WHEN AVG(user_satisfaction) >= 4.0 THEN 0.8
|
|
220
|
+
WHEN AVG(user_satisfaction) >= 3.5 THEN 0.7
|
|
221
|
+
WHEN AVG(user_satisfaction) >= 3.0 THEN 0.6
|
|
222
|
+
ELSE 0.5
|
|
223
|
+
END
|
|
224
|
+
FROM brand_interactions bi
|
|
225
|
+
WHERE bi.user_id = user_engagement_patterns.user_id
|
|
226
|
+
AND bi.created_at >= NOW() - INTERVAL '30 days'
|
|
227
|
+
), 0.5),
|
|
228
|
+
total_interactions = COALESCE((
|
|
229
|
+
SELECT COUNT(*)
|
|
230
|
+
FROM brand_interactions bi
|
|
231
|
+
WHERE bi.user_id = user_engagement_patterns.user_id
|
|
232
|
+
), 0),
|
|
233
|
+
last_interaction = COALESCE((
|
|
234
|
+
SELECT MAX(created_at)
|
|
235
|
+
FROM brand_interactions bi
|
|
236
|
+
WHERE bi.user_id = user_engagement_patterns.user_id
|
|
237
|
+
), last_interaction),
|
|
238
|
+
updated_at = NOW();
|
|
239
|
+
END;
|
|
240
|
+
$$ LANGUAGE plpgsql;
|
|
241
|
+
|
|
242
|
+
-- Triggers for automatic updates
|
|
243
|
+
CREATE OR REPLACE FUNCTION update_user_engagement_on_interaction()
|
|
244
|
+
RETURNS TRIGGER AS $$
|
|
245
|
+
BEGIN
|
|
246
|
+
INSERT INTO user_engagement_patterns (user_id, brand_name, total_interactions, last_interaction)
|
|
247
|
+
VALUES (NEW.user_id, NEW.brand_name, 1, NEW.created_at)
|
|
248
|
+
ON CONFLICT (user_id)
|
|
249
|
+
DO UPDATE SET
|
|
250
|
+
total_interactions = user_engagement_patterns.total_interactions + 1,
|
|
251
|
+
last_interaction = NEW.created_at,
|
|
252
|
+
updated_at = NOW();
|
|
253
|
+
|
|
254
|
+
RETURN NEW;
|
|
255
|
+
END;
|
|
256
|
+
$$ LANGUAGE plpgsql;
|
|
257
|
+
|
|
258
|
+
DROP TRIGGER IF EXISTS update_engagement_on_brand_interaction ON brand_interactions;
|
|
259
|
+
CREATE TRIGGER update_engagement_on_brand_interaction
|
|
260
|
+
AFTER INSERT ON brand_interactions
|
|
261
|
+
FOR EACH ROW
|
|
262
|
+
EXECUTE FUNCTION update_user_engagement_on_interaction();
|
|
263
|
+
|
|
264
|
+
-- Initialize circuit breaker states for known models
|
|
265
|
+
INSERT INTO circuit_breaker_states (model_name, state, threshold_failures, timeout_duration)
|
|
266
|
+
VALUES
|
|
267
|
+
('gemini20FlashExp', 'closed', 5, '5 minutes'),
|
|
268
|
+
('gemini15Flash', 'closed', 5, '5 minutes'),
|
|
269
|
+
('xai-grok-3-mini-fast', 'closed', 3, '10 minutes')
|
|
270
|
+
ON CONFLICT (model_name) DO NOTHING;
|
|
271
|
+
|
|
272
|
+
-- Create materialized views for expensive queries (refresh periodically)
|
|
273
|
+
CREATE MATERIALIZED VIEW IF NOT EXISTS daily_brand_performance AS
|
|
274
|
+
SELECT
|
|
275
|
+
brand_name,
|
|
276
|
+
DATE(created_at) as date,
|
|
277
|
+
COUNT(DISTINCT user_id) as unique_users,
|
|
278
|
+
COUNT(*) as total_interactions,
|
|
279
|
+
AVG(user_satisfaction) as avg_satisfaction,
|
|
280
|
+
COUNT(CASE WHEN interaction_type = 'product_inquiry' THEN 1 END) as product_inquiries,
|
|
281
|
+
COUNT(CASE WHEN interaction_type = 'store_locator' THEN 1 END) as store_requests,
|
|
282
|
+
COUNT(CASE WHEN user_satisfaction >= 4 THEN 1 END) as satisfied_users
|
|
283
|
+
FROM brand_interactions
|
|
284
|
+
WHERE created_at >= CURRENT_DATE - INTERVAL '90 days'
|
|
285
|
+
GROUP BY brand_name, DATE(created_at)
|
|
286
|
+
ORDER BY date DESC, brand_name;
|
|
287
|
+
|
|
288
|
+
-- Index on materialized view
|
|
289
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_daily_brand_performance_unique ON daily_brand_performance (brand_name, date);
|
|
290
|
+
|
|
291
|
+
-- Comments for documentation
|
|
292
|
+
COMMENT ON TABLE brand_interactions IS 'Tracks user interactions specific to each brand (Sienna Naturals, Myavana)';
|
|
293
|
+
COMMENT ON TABLE store_locator_requests IS 'Tracks store locator requests for physical location recommendations';
|
|
294
|
+
COMMENT ON TABLE response_cache IS 'Intelligent response caching with brand and user context';
|
|
295
|
+
COMMENT ON TABLE circuit_breaker_states IS 'Circuit breaker states for AI model reliability';
|
|
296
|
+
COMMENT ON TABLE smart_prompt_analytics IS 'Analytics for smart prompt generation effectiveness';
|
|
297
|
+
COMMENT ON TABLE user_engagement_patterns IS 'User engagement patterns and preferences by brand';
|
|
298
|
+
|
|
299
|
+
COMMENT ON FUNCTION cleanup_expired_cache() IS 'Removes expired cache entries, should be run periodically';
|
|
300
|
+
COMMENT ON FUNCTION update_engagement_patterns() IS 'Updates user engagement scores based on recent activity';
|
|
301
|
+
|
|
302
|
+
-- Grant permissions (adjust as needed for your setup)
|
|
303
|
+
-- GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO your_app_user;
|
|
304
|
+
-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO your_app_user;
|
|
305
|
+
-- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO your_app_user;
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
-- Complete Migration Runner Script
|
|
2
|
+
-- Run this to apply all migrations in the correct order
|
|
3
|
+
-- This script is safe to run multiple times (idempotent)
|
|
4
|
+
|
|
5
|
+
-- Migration tracking table
|
|
6
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
7
|
+
id SERIAL PRIMARY KEY,
|
|
8
|
+
migration_name VARCHAR(255) NOT NULL UNIQUE,
|
|
9
|
+
applied_at TIMESTAMP DEFAULT NOW(),
|
|
10
|
+
checksum VARCHAR(64)
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
-- Check if migration has been applied
|
|
14
|
+
CREATE OR REPLACE FUNCTION migration_applied(migration_name TEXT)
|
|
15
|
+
RETURNS BOOLEAN AS $$
|
|
16
|
+
BEGIN
|
|
17
|
+
RETURN EXISTS(SELECT 1 FROM schema_migrations WHERE schema_migrations.migration_name = migration_applied.migration_name);
|
|
18
|
+
END;
|
|
19
|
+
$$ LANGUAGE plpgsql;
|
|
20
|
+
|
|
21
|
+
-- Apply migration if not already applied
|
|
22
|
+
CREATE OR REPLACE FUNCTION apply_migration(migration_name TEXT, migration_checksum TEXT DEFAULT NULL)
|
|
23
|
+
RETURNS VOID AS $$
|
|
24
|
+
BEGIN
|
|
25
|
+
IF NOT migration_applied(migration_name) THEN
|
|
26
|
+
INSERT INTO schema_migrations (migration_name, checksum)
|
|
27
|
+
VALUES (migration_name, migration_checksum);
|
|
28
|
+
RAISE NOTICE 'Applied migration: %', migration_name;
|
|
29
|
+
ELSE
|
|
30
|
+
RAISE NOTICE 'Migration already applied: %', migration_name;
|
|
31
|
+
END IF;
|
|
32
|
+
END;
|
|
33
|
+
$$ LANGUAGE plpgsql;
|
|
34
|
+
|
|
35
|
+
-- Start migration process
|
|
36
|
+
DO $$
|
|
37
|
+
BEGIN
|
|
38
|
+
RAISE NOTICE 'Starting database migrations for Sienna Naturals Enhanced System...';
|
|
39
|
+
RAISE NOTICE 'Timestamp: %', NOW();
|
|
40
|
+
END $$;
|
|
41
|
+
|
|
42
|
+
-- Core tables (should already exist, but ensure they're there)
|
|
43
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
44
|
+
user_id VARCHAR(255) PRIMARY KEY,
|
|
45
|
+
name VARCHAR(255),
|
|
46
|
+
hair_type VARCHAR(100),
|
|
47
|
+
hair_concerns TEXT[],
|
|
48
|
+
location VARCHAR(255),
|
|
49
|
+
brand_preference VARCHAR(100) DEFAULT 'myavana',
|
|
50
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
51
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
55
|
+
id SERIAL PRIMARY KEY,
|
|
56
|
+
conversation_id VARCHAR(255) UNIQUE NOT NULL,
|
|
57
|
+
user_id VARCHAR(255) REFERENCES users(user_id),
|
|
58
|
+
chat_history JSONB,
|
|
59
|
+
summary TEXT,
|
|
60
|
+
brand VARCHAR(100) DEFAULT 'myavana',
|
|
61
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
62
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
66
|
+
session_id VARCHAR(255) PRIMARY KEY,
|
|
67
|
+
user_id VARCHAR(255),
|
|
68
|
+
session_data JSONB,
|
|
69
|
+
brand VARCHAR(100),
|
|
70
|
+
last_accessed_at TIMESTAMP DEFAULT NOW(),
|
|
71
|
+
expires_at TIMESTAMP DEFAULT NOW() + INTERVAL '7 days'
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
-- Core indexes
|
|
75
|
+
CREATE INDEX IF NOT EXISTS idx_users_brand_created ON users(brand_preference, created_at);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_user_brand ON conversations(user_id, brand, created_at);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_user_expires ON sessions(user_id, expires_at);
|
|
78
|
+
|
|
79
|
+
SELECT apply_migration('000_core_tables');
|
|
80
|
+
|
|
81
|
+
-- Migration 001: Analytics Tables
|
|
82
|
+
DO $$
|
|
83
|
+
BEGIN
|
|
84
|
+
IF NOT migration_applied('001_add_analytics_tables') THEN
|
|
85
|
+
RAISE NOTICE 'Applying Migration 001: Analytics Tables...';
|
|
86
|
+
|
|
87
|
+
-- All the analytics tables from 001_add_analytics_tables.sql
|
|
88
|
+
CREATE TABLE IF NOT EXISTS prompt_analytics (
|
|
89
|
+
id SERIAL PRIMARY KEY,
|
|
90
|
+
user_id VARCHAR(255) NOT NULL,
|
|
91
|
+
query_type VARCHAR(100),
|
|
92
|
+
topics_detected JSONB,
|
|
93
|
+
prompt_length INTEGER,
|
|
94
|
+
faqs_used INTEGER,
|
|
95
|
+
products_used INTEGER,
|
|
96
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
CREATE TABLE IF NOT EXISTS conversation_metrics (
|
|
100
|
+
id SERIAL PRIMARY KEY,
|
|
101
|
+
user_id VARCHAR(255) NOT NULL,
|
|
102
|
+
conversation_id VARCHAR(255) NOT NULL,
|
|
103
|
+
message_count INTEGER DEFAULT 0,
|
|
104
|
+
ai_model_used VARCHAR(100),
|
|
105
|
+
response_time_ms INTEGER,
|
|
106
|
+
user_satisfaction INTEGER,
|
|
107
|
+
conversion_event VARCHAR(100),
|
|
108
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
CREATE TABLE IF NOT EXISTS user_journey_events (
|
|
112
|
+
id SERIAL PRIMARY KEY,
|
|
113
|
+
user_id VARCHAR(255) NOT NULL,
|
|
114
|
+
event_type VARCHAR(100) NOT NULL,
|
|
115
|
+
event_data JSONB,
|
|
116
|
+
hair_journey_stage VARCHAR(100),
|
|
117
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
CREATE TABLE IF NOT EXISTS ai_model_performance (
|
|
121
|
+
id SERIAL PRIMARY KEY,
|
|
122
|
+
model_name VARCHAR(100) NOT NULL,
|
|
123
|
+
request_count INTEGER DEFAULT 1,
|
|
124
|
+
success_count INTEGER DEFAULT 0,
|
|
125
|
+
failure_count INTEGER DEFAULT 0,
|
|
126
|
+
avg_response_time_ms FLOAT,
|
|
127
|
+
date_tracked DATE DEFAULT CURRENT_DATE,
|
|
128
|
+
UNIQUE(model_name, date_tracked)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
CREATE TABLE IF NOT EXISTS cache_analytics (
|
|
132
|
+
id SERIAL PRIMARY KEY,
|
|
133
|
+
cache_type VARCHAR(100) NOT NULL,
|
|
134
|
+
hit_count INTEGER DEFAULT 0,
|
|
135
|
+
miss_count INTEGER DEFAULT 0,
|
|
136
|
+
cache_key_pattern VARCHAR(255),
|
|
137
|
+
date_tracked DATE DEFAULT CURRENT_DATE,
|
|
138
|
+
UNIQUE(cache_type, cache_key_pattern, date_tracked)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
-- Indexes
|
|
142
|
+
CREATE INDEX IF NOT EXISTS idx_prompt_analytics_user_created ON prompt_analytics(user_id, created_at);
|
|
143
|
+
CREATE INDEX IF NOT EXISTS idx_conversation_metrics_user ON conversation_metrics(user_id, created_at);
|
|
144
|
+
CREATE INDEX IF NOT EXISTS idx_user_journey_events_user ON user_journey_events(user_id, created_at);
|
|
145
|
+
CREATE INDEX IF NOT EXISTS idx_ai_model_performance_date ON ai_model_performance(date_tracked);
|
|
146
|
+
|
|
147
|
+
-- Views
|
|
148
|
+
CREATE OR REPLACE VIEW daily_conversation_stats AS
|
|
149
|
+
SELECT
|
|
150
|
+
DATE(created_at) as date,
|
|
151
|
+
COUNT(DISTINCT user_id) as unique_users,
|
|
152
|
+
COUNT(*) as total_conversations,
|
|
153
|
+
AVG(message_count) as avg_messages_per_conversation,
|
|
154
|
+
COUNT(CASE WHEN conversion_event IS NOT NULL THEN 1 END) as conversions
|
|
155
|
+
FROM conversation_metrics
|
|
156
|
+
GROUP BY DATE(created_at)
|
|
157
|
+
ORDER BY date DESC;
|
|
158
|
+
|
|
159
|
+
CREATE OR REPLACE VIEW model_reliability_stats AS
|
|
160
|
+
SELECT
|
|
161
|
+
model_name,
|
|
162
|
+
SUM(request_count) as total_requests,
|
|
163
|
+
SUM(success_count) as total_successes,
|
|
164
|
+
SUM(failure_count) as total_failures,
|
|
165
|
+
ROUND((SUM(success_count)::float / SUM(request_count)::float * 100), 2) as success_rate,
|
|
166
|
+
ROUND(AVG(avg_response_time_ms), 2) as avg_response_time
|
|
167
|
+
FROM ai_model_performance
|
|
168
|
+
WHERE date_tracked >= CURRENT_DATE - INTERVAL '30 days'
|
|
169
|
+
GROUP BY model_name;
|
|
170
|
+
|
|
171
|
+
SELECT apply_migration('001_add_analytics_tables');
|
|
172
|
+
END IF;
|
|
173
|
+
END $$;
|
|
174
|
+
|
|
175
|
+
-- Migration 002: Advanced Features
|
|
176
|
+
DO $$
|
|
177
|
+
BEGIN
|
|
178
|
+
IF NOT migration_applied('002_add_advanced_features_tables') THEN
|
|
179
|
+
RAISE NOTICE 'Applying Migration 002: Advanced Features Tables...';
|
|
180
|
+
|
|
181
|
+
-- All tables from 002_add_advanced_features_tables.sql
|
|
182
|
+
CREATE TABLE IF NOT EXISTS error_logs (
|
|
183
|
+
id SERIAL PRIMARY KEY,
|
|
184
|
+
user_id VARCHAR(255) NOT NULL,
|
|
185
|
+
conversation_id VARCHAR(255),
|
|
186
|
+
error_type VARCHAR(100) NOT NULL,
|
|
187
|
+
error_message TEXT,
|
|
188
|
+
model_name VARCHAR(100),
|
|
189
|
+
context_data JSONB,
|
|
190
|
+
recovery_attempted BOOLEAN DEFAULT FALSE,
|
|
191
|
+
recovery_successful BOOLEAN DEFAULT FALSE,
|
|
192
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
CREATE TABLE IF NOT EXISTS personalization_metrics (
|
|
196
|
+
id SERIAL PRIMARY KEY,
|
|
197
|
+
user_id VARCHAR(255) NOT NULL,
|
|
198
|
+
persona_assigned VARCHAR(100),
|
|
199
|
+
query_length INTEGER,
|
|
200
|
+
response_length INTEGER,
|
|
201
|
+
personalization_applied BOOLEAN DEFAULT FALSE,
|
|
202
|
+
user_satisfaction_score INTEGER,
|
|
203
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
CREATE TABLE IF NOT EXISTS user_preferences (
|
|
207
|
+
id SERIAL PRIMARY KEY,
|
|
208
|
+
user_id VARCHAR(255) NOT NULL UNIQUE,
|
|
209
|
+
persona_type VARCHAR(100),
|
|
210
|
+
communication_style VARCHAR(100),
|
|
211
|
+
preferred_response_format VARCHAR(100),
|
|
212
|
+
hair_priorities JSONB,
|
|
213
|
+
product_preferences JSONB,
|
|
214
|
+
engagement_preferences JSONB,
|
|
215
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
216
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
CREATE TABLE IF NOT EXISTS proactive_campaigns (
|
|
220
|
+
id SERIAL PRIMARY KEY,
|
|
221
|
+
campaign_name VARCHAR(255) NOT NULL,
|
|
222
|
+
campaign_type VARCHAR(100) NOT NULL,
|
|
223
|
+
target_persona VARCHAR(100),
|
|
224
|
+
trigger_conditions JSONB,
|
|
225
|
+
message_template JSONB,
|
|
226
|
+
active BOOLEAN DEFAULT TRUE,
|
|
227
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
CREATE TABLE IF NOT EXISTS proactive_engagement_schedule (
|
|
231
|
+
id SERIAL PRIMARY KEY,
|
|
232
|
+
user_id VARCHAR(255) NOT NULL,
|
|
233
|
+
trigger_type VARCHAR(100) NOT NULL,
|
|
234
|
+
scheduled_time TIMESTAMP NOT NULL,
|
|
235
|
+
context_data JSONB,
|
|
236
|
+
sent BOOLEAN DEFAULT FALSE,
|
|
237
|
+
sent_at TIMESTAMP,
|
|
238
|
+
active BOOLEAN DEFAULT TRUE,
|
|
239
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
240
|
+
UNIQUE(user_id, trigger_type)
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
CREATE TABLE IF NOT EXISTS proactive_engagement_history (
|
|
244
|
+
id SERIAL PRIMARY KEY,
|
|
245
|
+
user_id VARCHAR(255) NOT NULL,
|
|
246
|
+
campaign_id INTEGER REFERENCES proactive_campaigns(id),
|
|
247
|
+
message_sent JSONB,
|
|
248
|
+
delivery_status VARCHAR(50) DEFAULT 'pending',
|
|
249
|
+
user_response TEXT,
|
|
250
|
+
engagement_score FLOAT,
|
|
251
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
252
|
+
responded_at TIMESTAMP
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
CREATE TABLE IF NOT EXISTS hair_issues (
|
|
256
|
+
id SERIAL PRIMARY KEY,
|
|
257
|
+
user_id VARCHAR(255) NOT NULL,
|
|
258
|
+
issue_description TEXT NOT NULL,
|
|
259
|
+
severity_level INTEGER,
|
|
260
|
+
issue_category VARCHAR(100),
|
|
261
|
+
reported_context TEXT,
|
|
262
|
+
resolution_status VARCHAR(50) DEFAULT 'open',
|
|
263
|
+
resolution_notes TEXT,
|
|
264
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
265
|
+
resolved_at TIMESTAMP
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
-- Indexes
|
|
269
|
+
CREATE INDEX IF NOT EXISTS idx_error_logs_user_created ON error_logs(user_id, created_at);
|
|
270
|
+
CREATE INDEX IF NOT EXISTS idx_error_logs_type_model ON error_logs(error_type, model_name);
|
|
271
|
+
CREATE INDEX IF NOT EXISTS idx_personalization_metrics_user ON personalization_metrics(user_id, created_at);
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_user_preferences_user ON user_preferences(user_id);
|
|
273
|
+
CREATE INDEX IF NOT EXISTS idx_proactive_engagement_user ON proactive_engagement_history(user_id, created_at);
|
|
274
|
+
CREATE INDEX IF NOT EXISTS idx_hair_issues_user_status ON hair_issues(user_id, resolution_status);
|
|
275
|
+
|
|
276
|
+
-- Update trigger for user_preferences
|
|
277
|
+
CREATE OR REPLACE FUNCTION update_user_preferences_updated_at()
|
|
278
|
+
RETURNS TRIGGER AS $func$
|
|
279
|
+
BEGIN
|
|
280
|
+
NEW.updated_at = NOW();
|
|
281
|
+
RETURN NEW;
|
|
282
|
+
END;
|
|
283
|
+
$func$ LANGUAGE plpgsql;
|
|
284
|
+
|
|
285
|
+
DROP TRIGGER IF EXISTS update_user_preferences_updated_at_trigger ON user_preferences;
|
|
286
|
+
CREATE TRIGGER update_user_preferences_updated_at_trigger
|
|
287
|
+
BEFORE UPDATE ON user_preferences
|
|
288
|
+
FOR EACH ROW
|
|
289
|
+
EXECUTE FUNCTION update_user_preferences_updated_at();
|
|
290
|
+
|
|
291
|
+
SELECT apply_migration('002_add_advanced_features_tables');
|
|
292
|
+
END IF;
|
|
293
|
+
END $$;
|
|
294
|
+
|
|
295
|
+
-- Migration 003: Sienna Naturals Enhancements
|
|
296
|
+
DO $$
|
|
297
|
+
BEGIN
|
|
298
|
+
IF NOT migration_applied('003_sienna_naturals_enhancements') THEN
|
|
299
|
+
RAISE NOTICE 'Applying Migration 003: Sienna Naturals Enhancements...';
|
|
300
|
+
|
|
301
|
+
-- Brand-specific tables
|
|
302
|
+
CREATE TABLE IF NOT EXISTS brand_interactions (
|
|
303
|
+
id SERIAL PRIMARY KEY,
|
|
304
|
+
user_id VARCHAR(255) NOT NULL,
|
|
305
|
+
conversation_id VARCHAR(255),
|
|
306
|
+
brand_name VARCHAR(100) NOT NULL,
|
|
307
|
+
interaction_type VARCHAR(100),
|
|
308
|
+
interaction_data JSONB,
|
|
309
|
+
user_satisfaction INTEGER,
|
|
310
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
CREATE TABLE IF NOT EXISTS store_locator_requests (
|
|
314
|
+
id SERIAL PRIMARY KEY,
|
|
315
|
+
user_id VARCHAR(255) NOT NULL,
|
|
316
|
+
location_query VARCHAR(255),
|
|
317
|
+
user_location JSONB,
|
|
318
|
+
stores_found INTEGER DEFAULT 0,
|
|
319
|
+
stores_data JSONB,
|
|
320
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
CREATE TABLE IF NOT EXISTS circuit_breaker_states (
|
|
324
|
+
id SERIAL PRIMARY KEY,
|
|
325
|
+
model_name VARCHAR(100) NOT NULL UNIQUE,
|
|
326
|
+
state VARCHAR(20) DEFAULT 'closed',
|
|
327
|
+
failure_count INTEGER DEFAULT 0,
|
|
328
|
+
last_failure_time TIMESTAMP,
|
|
329
|
+
last_success_time TIMESTAMP,
|
|
330
|
+
threshold_failures INTEGER DEFAULT 5,
|
|
331
|
+
timeout_duration INTERVAL DEFAULT '5 minutes',
|
|
332
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
CREATE TABLE IF NOT EXISTS response_cache (
|
|
336
|
+
id SERIAL PRIMARY KEY,
|
|
337
|
+
cache_key VARCHAR(512) NOT NULL UNIQUE,
|
|
338
|
+
brand_name VARCHAR(100),
|
|
339
|
+
cached_response JSONB NOT NULL,
|
|
340
|
+
user_context_hash VARCHAR(64),
|
|
341
|
+
query_hash VARCHAR(64),
|
|
342
|
+
context_analysis JSONB,
|
|
343
|
+
hit_count INTEGER DEFAULT 0,
|
|
344
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
345
|
+
expires_at TIMESTAMP DEFAULT NOW() + INTERVAL '24 hours'
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
CREATE TABLE IF NOT EXISTS user_engagement_patterns (
|
|
349
|
+
id SERIAL PRIMARY KEY,
|
|
350
|
+
user_id VARCHAR(255) NOT NULL UNIQUE,
|
|
351
|
+
brand_name VARCHAR(100),
|
|
352
|
+
total_interactions INTEGER DEFAULT 0,
|
|
353
|
+
avg_session_duration INTERVAL,
|
|
354
|
+
preferred_interaction_time TIME,
|
|
355
|
+
preferred_day_of_week INTEGER,
|
|
356
|
+
response_style_preference VARCHAR(100),
|
|
357
|
+
topic_preferences JSONB,
|
|
358
|
+
engagement_score FLOAT DEFAULT 0,
|
|
359
|
+
last_interaction TIMESTAMP,
|
|
360
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
-- Indexes
|
|
364
|
+
CREATE INDEX IF NOT EXISTS idx_brand_interactions_user_brand ON brand_interactions(user_id, brand_name, created_at);
|
|
365
|
+
CREATE INDEX IF NOT EXISTS idx_response_cache_key ON response_cache(cache_key, expires_at) WHERE expires_at > NOW();
|
|
366
|
+
CREATE INDEX IF NOT EXISTS idx_circuit_breaker_model ON circuit_breaker_states(model_name, state);
|
|
367
|
+
CREATE INDEX IF NOT EXISTS idx_user_engagement_brand ON user_engagement_patterns(brand_name, engagement_score DESC);
|
|
368
|
+
|
|
369
|
+
-- Initialize circuit breaker states
|
|
370
|
+
INSERT INTO circuit_breaker_states (model_name, state, threshold_failures, timeout_duration)
|
|
371
|
+
VALUES
|
|
372
|
+
('gemini20FlashExp', 'closed', 5, '5 minutes'),
|
|
373
|
+
('gemini15Flash', 'closed', 5, '5 minutes'),
|
|
374
|
+
('xai-grok-3-mini-fast', 'closed', 3, '10 minutes')
|
|
375
|
+
ON CONFLICT (model_name) DO NOTHING;
|
|
376
|
+
|
|
377
|
+
-- Trigger for engagement patterns
|
|
378
|
+
CREATE OR REPLACE FUNCTION update_user_engagement_on_interaction()
|
|
379
|
+
RETURNS TRIGGER AS $func$
|
|
380
|
+
BEGIN
|
|
381
|
+
INSERT INTO user_engagement_patterns (user_id, brand_name, total_interactions, last_interaction)
|
|
382
|
+
VALUES (NEW.user_id, NEW.brand_name, 1, NEW.created_at)
|
|
383
|
+
ON CONFLICT (user_id)
|
|
384
|
+
DO UPDATE SET
|
|
385
|
+
total_interactions = user_engagement_patterns.total_interactions + 1,
|
|
386
|
+
last_interaction = NEW.created_at,
|
|
387
|
+
updated_at = NOW();
|
|
388
|
+
|
|
389
|
+
RETURN NEW;
|
|
390
|
+
END;
|
|
391
|
+
$func$ LANGUAGE plpgsql;
|
|
392
|
+
|
|
393
|
+
DROP TRIGGER IF EXISTS update_engagement_on_brand_interaction ON brand_interactions;
|
|
394
|
+
CREATE TRIGGER update_engagement_on_brand_interaction
|
|
395
|
+
AFTER INSERT ON brand_interactions
|
|
396
|
+
FOR EACH ROW
|
|
397
|
+
EXECUTE FUNCTION update_user_engagement_on_interaction();
|
|
398
|
+
|
|
399
|
+
SELECT apply_migration('003_sienna_naturals_enhancements');
|
|
400
|
+
END IF;
|
|
401
|
+
END $$;
|
|
402
|
+
|
|
403
|
+
-- Final cleanup and optimization
|
|
404
|
+
DO $$
|
|
405
|
+
BEGIN
|
|
406
|
+
RAISE NOTICE 'Running post-migration optimizations...';
|
|
407
|
+
|
|
408
|
+
-- Update statistics
|
|
409
|
+
ANALYZE;
|
|
410
|
+
|
|
411
|
+
-- Clean up any expired cache entries
|
|
412
|
+
DELETE FROM response_cache WHERE expires_at < NOW();
|
|
413
|
+
|
|
414
|
+
RAISE NOTICE 'All migrations completed successfully!';
|
|
415
|
+
RAISE NOTICE 'Database is now ready for Sienna Naturals Enhanced System';
|
|
416
|
+
|
|
417
|
+
-- Show migration status
|
|
418
|
+
RAISE NOTICE 'Applied migrations:';
|
|
419
|
+
FOR rec IN SELECT migration_name, applied_at FROM schema_migrations ORDER BY applied_at LOOP
|
|
420
|
+
RAISE NOTICE ' - % (applied: %)', rec.migration_name, rec.applied_at;
|
|
421
|
+
END LOOP;
|
|
422
|
+
END $$;
|
|
423
|
+
|
|
424
|
+
-- Verification queries
|
|
425
|
+
SELECT 'Migration Status' as info;
|
|
426
|
+
SELECT migration_name, applied_at FROM schema_migrations ORDER BY applied_at;
|
|
427
|
+
|
|
428
|
+
SELECT 'Table Counts' as info;
|
|
429
|
+
SELECT
|
|
430
|
+
schemaname as schema,
|
|
431
|
+
tablename as table_name,
|
|
432
|
+
n_tup_ins as inserts,
|
|
433
|
+
n_tup_upd as updates,
|
|
434
|
+
n_tup_del as deletes
|
|
435
|
+
FROM pg_stat_user_tables
|
|
436
|
+
WHERE schemaname = 'public'
|
|
437
|
+
ORDER BY tablename;
|
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myavana-bot-test-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"description": "Shared bot functionality with enhanced features",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test:watch": "jest --watch",
|
|
9
|
+
"test:coverage": "jest --coverage",
|
|
10
|
+
"lint": "eslint src/",
|
|
11
|
+
"lint:fix": "eslint src/ --fix"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
"@genkit-ai/googleai": "^0.9.12",
|
|
15
|
+
"dotenv": "^16.4.7",
|
|
16
|
+
"express-rate-limit": "^7.1.5",
|
|
17
|
+
"express-slow-down": "^2.0.1",
|
|
18
|
+
"genkit": "^0.9.12",
|
|
19
|
+
"isomorphic-dompurify": "^2.9.0",
|
|
20
|
+
"joi": "^17.11.0",
|
|
21
|
+
"node-cache": "^5.1.2",
|
|
22
|
+
"pg": "^8.13.1",
|
|
23
|
+
"redis": "^4.7.0",
|
|
24
|
+
"uuid": "^9.0.1",
|
|
25
|
+
"winston": "^3.11.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
"@babel/preset-env": "^7.23.6",
|
|
29
|
+
"babel-jest": "^29.7.0",
|
|
30
|
+
"eslint": "^8.56.0",
|
|
31
|
+
"jest": "^29.7.0",
|
|
32
|
+
"supertest": "^6.3.3"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
|
-
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
}
|
package/src/ai.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// ai.js
|
|
2
2
|
const { genkit } = require('genkit');
|
|
3
|
-
const { googleAI, gemini15Flash, gemini20FlashExp } = require('@genkit-ai/googleai');
|
|
3
|
+
const { googleAI, gemini15Flash, gemini20FlashExp, gemini } = require('@genkit-ai/googleai');
|
|
4
4
|
const config = require('./config');
|
|
5
5
|
|
|
6
6
|
const ai = genkit({
|
|
7
7
|
plugins: [googleAI({
|
|
8
8
|
apiKey: config.genkitApiKey,
|
|
9
9
|
})],
|
|
10
|
-
model:
|
|
10
|
+
model: gemini('gemini-3-flash-preview'),
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
module.exports = ai;
|
package/src/conversation.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// conversation.js
|
|
2
2
|
const { pgClient, redisClient } = require('./database');
|
|
3
3
|
const ai = require('./ai');
|
|
4
|
-
const {gemini15Flash} = require("@genkit-ai/googleai");
|
|
4
|
+
const {gemini15Flash, gemini} = require("@genkit-ai/googleai");
|
|
5
5
|
|
|
6
6
|
const saveConversationSummary = async (conversationId, userId, summary) => {
|
|
7
7
|
const query = `
|
|
@@ -28,7 +28,7 @@ const generateConversationSummary = async (chatHistory) => {
|
|
|
28
28
|
}
|
|
29
29
|
const summaryPrompt = `Summarize the following conversation, getting all important information, especially the user's hair details, name, and location: ${JSON.stringify(chatHistory)}`;
|
|
30
30
|
const { text } = await ai.chat({
|
|
31
|
-
model:
|
|
31
|
+
model: gemini('gemini-2.5-flash'),
|
|
32
32
|
system: summaryPrompt,
|
|
33
33
|
});
|
|
34
34
|
return text;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// postResponseProcessing.js
|
|
2
2
|
const ai = require("./ai");
|
|
3
|
-
const {gemini15Flash} = require("@genkit-ai/googleai");
|
|
3
|
+
const {gemini15Flash, gemini} = require("@genkit-ai/googleai");
|
|
4
4
|
const DatabaseSessionStore = require("./session");
|
|
5
5
|
const { pgClient, redisClient } = require('./database');
|
|
6
6
|
const {generateAndSaveSummary, saveConversationSummary, saveConversation} = require("./conversation");
|
|
@@ -226,7 +226,7 @@ const postProcessConversation = async (chatHistory, message) => {
|
|
|
226
226
|
//console.log('Extraction prompt: ', extractionPrompt);
|
|
227
227
|
try {
|
|
228
228
|
const chat = ai.chat({
|
|
229
|
-
model:
|
|
229
|
+
model: gemini('gemini-2.5-flash'),
|
|
230
230
|
config: {
|
|
231
231
|
temperature: 1.1,
|
|
232
232
|
},
|
|
@@ -385,7 +385,7 @@ const postResponseProductCheck = async (chatHistory, message) => {
|
|
|
385
385
|
// Try Gemini first
|
|
386
386
|
try {
|
|
387
387
|
const chat = ai.chat({
|
|
388
|
-
model:
|
|
388
|
+
model: gemini('gemini-2.5-flash'), // or gemini20FlashExp
|
|
389
389
|
config: {
|
|
390
390
|
temperature: 0.3, // Reduced temperature for more consistent JSON formatting
|
|
391
391
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// unifiedChatHandler.js - Fixed version with proper model prioritization
|
|
2
2
|
const { genkit, z } = require('genkit');
|
|
3
|
-
const { gemini15Flash, gemini15Pro, gemini20FlashExp } = require('@genkit-ai/googleai');
|
|
3
|
+
const { gemini15Flash, gemini15Pro, gemini20FlashExp, gemini } = require('@genkit-ai/googleai');
|
|
4
4
|
|
|
5
5
|
class UnifiedChatHandler {
|
|
6
6
|
constructor(config) {
|
|
@@ -17,9 +17,9 @@ class UnifiedChatHandler {
|
|
|
17
17
|
|
|
18
18
|
// Initialize model priority list - GEMINI FIRST!
|
|
19
19
|
this.modelPriority = [
|
|
20
|
-
{ name: 'gemini20FlashExp', model:
|
|
21
|
-
{ name: 'gemini15Flash', model:
|
|
22
|
-
{ name: 'gemini15Pro', model:
|
|
20
|
+
{ name: 'gemini20FlashExp', model: gemini('gemini-3-flash-preview'), type: 'primary' },
|
|
21
|
+
{ name: 'gemini15Flash', model: gemini('gemini-2.5-flash'), type: 'primary' },
|
|
22
|
+
{ name: 'gemini15Pro', model: gemini('gemini-3-pro-preview'), type: 'backup' }
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
// Only initialize xAI as last resort if API key exists
|