ayush2917 commited on
Commit
2a1bd9a
·
verified ·
1 Parent(s): 2f0747a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -94
app.py CHANGED
@@ -1,129 +1,173 @@
1
- from flask import Flask, render_template, request, jsonify
2
- import yaml
3
- from datetime import datetime
4
- import threading
5
- import logging
6
  import os
7
  import time
 
 
 
 
 
 
8
  import stat
9
 
 
10
  app = Flask(__name__)
11
 
 
 
 
 
 
12
  # Configure logging
13
- logging.basicConfig(level=logging.INFO)
 
 
 
14
  logger = logging.getLogger(__name__)
15
 
16
- # Sample default documents if file is missing
 
 
 
 
 
 
17
  DEFAULT_DOCUMENTS = [
18
  {"content": "Rupeia offers investment plans with returns between 5-10%", "category": "investments"},
19
  {"content": "You can set up savings goals in the mobile app", "category": "goals"}
20
  ]
21
 
22
- # Load configuration
23
- try:
24
- with open('config/config.yaml', 'r') as f:
25
- config = yaml.safe_load(f)
26
- except Exception as e:
27
- logger.warning(f"Config load failed: {str(e)}")
28
- config = {
29
- 'quick_replies': [
30
- {"title": "Investments", "value": "investment options"},
31
- {"title": "Savings", "value": "saving goals"}
32
- ]
33
- }
34
 
35
- # Model loading status
36
- models_loaded = False
37
- loading_error = None
 
 
 
 
 
38
 
39
- def load_models_with_retries(max_retries=3, retry_delay=5, timeout=120):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  global models_loaded, loading_error
41
- start_time = time.time()
42
- for attempt in range(max_retries):
43
- try:
44
- logger.info(f"Attempt {attempt + 1}/{max_retries}: Starting model loading...")
45
-
46
- # Log cache contents for debugging
47
- cache_folder = '/app/cache'
48
- if os.path.exists(cache_folder):
49
- logger.info(f"Cache folder contents: {os.listdir(cache_folder)}")
50
-
51
- # Import inside function to prevent circular imports
52
- from src.retrieval import DocumentRetriever
53
- from src.generation import ResponseGenerator
54
-
55
- # Check if data file exists, create if not
56
- data_path = 'data/rupeia_document.json'
57
- if not os.path.exists(data_path):
58
- os.makedirs('data', exist_ok=True)
59
- with open(data_path, 'w') as f:
60
- import json
61
- json.dump(DEFAULT_DOCUMENTS, f)
62
- # Set permissions for the file
63
- os.chmod(data_path, stat.S_IRUSR | stat.S_IWUSR)
64
- logger.info(f"Created default data file with permissions: {oct(os.stat(data_path).st_mode)[-3:]}")
65
-
66
- # Initialize models with explicit cache directory
67
- app.retriever = DocumentRetriever(cache_folder=cache_folder)
68
- app.generator = ResponseGenerator(cache_folder=cache_folder)
69
- models_loaded = True
70
- logger.info(f"Models loaded successfully in {time.time() - start_time:.2f} seconds")
71
- return
72
- except Exception as e:
73
- loading_error = str(e)
74
- logger.error(f"Model loading failed: {loading_error}")
75
- if attempt < max_retries - 1:
76
- logger.info(f"Retrying in {retry_delay} seconds...")
77
- time.sleep(retry_delay)
78
- if time.time() - start_time > timeout:
79
- logger.error("Model loading timed out")
80
- break
81
-
82
- logger.error("All retry attempts failed. Models not loaded.")
83
-
84
- # Start loading in background
85
- threading.Thread(target=load_models_with_retries, daemon=True).start()
86
 
87
  @app.route('/')
88
  def home():
89
- return render_template('index.html')
 
90
 
91
  @app.route('/health')
92
  def health():
93
- """Check if the application is ready to process requests."""
94
- status = "ready" if models_loaded else "initializing"
95
- return jsonify({
96
- "status": status,
97
- "error": loading_error if loading_error else None,
98
- "uptime": time.time() - app.start_time
99
- })
 
 
 
 
 
 
 
100
 
101
  @app.route('/api/chat', methods=['POST'])
102
  def chat():
 
103
  if not models_loaded:
104
- message = "System is initializing..."
105
- if loading_error:
106
- message = f"System error: {loading_error}"
107
  return jsonify({
108
- 'text': message,
109
  'quickReplies': config.get('quick_replies', []),
110
- 'conversation_id': str(datetime.now().timestamp())
111
- })
112
-
113
- user_message = request.json.get('message', '')
114
  try:
 
 
 
 
115
  context = app.retriever.retrieve(user_message)
116
  response = app.generator.generate(user_message, context)
 
 
 
 
 
 
 
 
117
  except Exception as e:
118
- logger.error(f"Error generating response: {str(e)}")
119
- response = "Sorry, I encountered an error processing your request"
120
-
121
- return jsonify({
122
- 'text': response,
123
- 'quickReplies': config.get('quick_replies', []),
124
- 'conversation_id': str(datetime.now().timestamp())
125
- })
126
 
127
  if __name__ == '__main__':
128
- app.start_time = time.time()
129
- app.run(host='0.0.0.0', port=8000)
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import time
3
+ import json
4
+ import logging
5
+ from threading import Event
6
+ from flask import Flask, request, jsonify, render_template
7
+ import yaml
8
+ from datetime import datetime
9
  import stat
10
 
11
+ # Initialize Flask app
12
  app = Flask(__name__)
13
 
14
+ # Configuration
15
+ CONFIG_PATH = 'config/config.yaml'
16
+ DATA_PATH = 'data/rupeia_document.json'
17
+ CACHE_DIR = '/app/cache'
18
+
19
  # Configure logging
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
23
+ )
24
  logger = logging.getLogger(__name__)
25
 
26
+ # Global state
27
+ models_loaded = False
28
+ loading_event = Event()
29
+ loading_error = None
30
+ app_start_time = time.time()
31
+
32
+ # Default data
33
  DEFAULT_DOCUMENTS = [
34
  {"content": "Rupeia offers investment plans with returns between 5-10%", "category": "investments"},
35
  {"content": "You can set up savings goals in the mobile app", "category": "goals"}
36
  ]
37
 
38
+ DEFAULT_CONFIG = {
39
+ 'quick_replies': [
40
+ {"title": "Investments", "value": "investment options"},
41
+ {"title": "Savings", "value": "saving goals"}
42
+ ]
43
+ }
 
 
 
 
 
 
44
 
45
+ def load_config():
46
+ """Load configuration with fallback to defaults"""
47
+ try:
48
+ with open(CONFIG_PATH, 'r') as f:
49
+ return yaml.safe_load(f) or DEFAULT_CONFIG
50
+ except Exception as e:
51
+ logger.warning(f"Config load failed: {str(e)}")
52
+ return DEFAULT_CONFIG
53
 
54
+ config = load_config()
55
+
56
+ def initialize_data_file():
57
+ """Ensure data file exists with proper permissions"""
58
+ try:
59
+ if not os.path.exists(DATA_PATH):
60
+ os.makedirs('data', exist_ok=True)
61
+ with open(DATA_PATH, 'w') as f:
62
+ json.dump(DEFAULT_DOCUMENTS, f)
63
+ os.chmod(DATA_PATH, stat.S_IRUSR | stat.S_IWUSR)
64
+ logger.info(f"Initialized data file at {DATA_PATH}")
65
+ except Exception as e:
66
+ logger.error(f"Data file initialization failed: {str(e)}")
67
+ raise
68
+
69
+ def load_models():
70
+ """Synchronously load ML models with proper error handling"""
71
  global models_loaded, loading_error
72
+
73
+ try:
74
+ logger.info("Starting model initialization...")
75
+ start_time = time.time()
76
+
77
+ # Verify cache accessibility
78
+ if not os.access(CACHE_DIR, os.W_OK):
79
+ raise PermissionError(f"Cannot write to cache directory: {CACHE_DIR}")
80
+
81
+ # Initialize data file
82
+ initialize_data_file()
83
+
84
+ # Dynamic imports to prevent circular dependencies
85
+ from src.retrieval import DocumentRetriever
86
+ from src.generation import ResponseGenerator
87
+
88
+ # Initialize models
89
+ app.retriever = DocumentRetriever(cache_folder=CACHE_DIR)
90
+ app.generator = ResponseGenerator(cache_folder=CACHE_DIR)
91
+
92
+ models_loaded = True
93
+ logger.info(f"Models loaded successfully in {time.time() - start_time:.2f} seconds")
94
+
95
+ except Exception as e:
96
+ loading_error = str(e)
97
+ logger.error(f"Model loading failed: {loading_error}")
98
+ raise
99
+ finally:
100
+ loading_event.set()
101
+
102
+ @app.before_first_request
103
+ def before_first_request():
104
+ """Ensure models are loading when first request comes in"""
105
+ if not loading_event.is_set():
106
+ logger.warning("Models not yet loaded when request received")
107
+ loading_event.wait()
 
 
 
 
 
 
 
 
 
108
 
109
  @app.route('/')
110
  def home():
111
+ """Render main interface"""
112
+ return render_template('index.html', quick_replies=config.get('quick_replies', []))
113
 
114
  @app.route('/health')
115
  def health():
116
+ """Comprehensive health check endpoint"""
117
+ status = {
118
+ "status": "ready" if models_loaded else "initializing",
119
+ "uptime_seconds": round(time.time() - app_start_time, 2),
120
+ "models_loaded": models_loaded,
121
+ "last_error": loading_error,
122
+ "system": {
123
+ "cache_accessible": os.access(CACHE_DIR, os.R_OK | os.W_OK),
124
+ "data_file_exists": os.path.exists(DATA_PATH),
125
+ "memory_usage_mb": os.sys.getsizeof({}) // (1024 * 1024) # Placeholder
126
+ },
127
+ "timestamp": datetime.utcnow().isoformat()
128
+ }
129
+ return jsonify(status), 200 if models_loaded else 503
130
 
131
  @app.route('/api/chat', methods=['POST'])
132
  def chat():
133
+ """Handle chat requests with proper error handling"""
134
  if not models_loaded:
 
 
 
135
  return jsonify({
136
+ 'text': loading_error or "System is initializing, please try again shortly",
137
  'quickReplies': config.get('quick_replies', []),
138
+ 'conversation_id': str(datetime.utcnow().timestamp()),
139
+ 'status': 'initializing'
140
+ }), 503
141
+
142
  try:
143
+ user_message = request.json.get('message', '').strip()
144
+ if not user_message:
145
+ raise ValueError("Empty message received")
146
+
147
  context = app.retriever.retrieve(user_message)
148
  response = app.generator.generate(user_message, context)
149
+
150
+ return jsonify({
151
+ 'text': response,
152
+ 'quickReplies': config.get('quick_replies', []),
153
+ 'conversation_id': str(datetime.utcnow().timestamp()),
154
+ 'status': 'success'
155
+ })
156
+
157
  except Exception as e:
158
+ logger.error(f"Chat error: {str(e)}", exc_info=True)
159
+ return jsonify({
160
+ 'text': "Sorry, I encountered an error processing your request",
161
+ 'quickReplies': config.get('quick_replies', []),
162
+ 'conversation_id': str(datetime.utcnow().timestamp()),
163
+ 'status': 'error',
164
+ 'error': str(e)
165
+ }), 500
166
 
167
  if __name__ == '__main__':
168
+ # Start model loading in main thread for gunicorn compatibility
169
+ load_models()
170
+ app.run(host='0.0.0.0', port=8000, threaded=True)
171
+ else:
172
+ # When running under Gunicorn
173
+ load_models()