NitinBot001 commited on
Commit
2ca9f67
·
verified ·
1 Parent(s): f0312aa

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +382 -141
server.py CHANGED
@@ -1,141 +1,382 @@
1
- # flask_app.py
2
-
3
- from flask import Flask, request, jsonify, render_template
4
- from app import EasyFarmsAssistant
5
- from conversation_manager import ConversationManager # Make sure this import is present
6
- import logging
7
- import uuid
8
- import os
9
- from flask_cors import CORS
10
-
11
- # Configure logging
12
- logging.basicConfig(level=logging.INFO)
13
- logger = logging.getLogger(__name__)
14
-
15
- # Initialize the Flask application
16
- app = Flask(__name__)
17
- CORS(app)
18
-
19
- # --- Initialize Core Services ---
20
- # This setup assumes your .env file is correctly configured with Supabase credentials.
21
- try:
22
- conv_manager = ConversationManager()
23
- assistant = EasyFarmsAssistant(manager=conv_manager)
24
- logger.info("EasyFarmsAssistant and ConversationManager initialized successfully.")
25
- except Exception as e:
26
- logger.error(f"FATAL: Could not initialize services. Error: {e}")
27
- assistant = None
28
- conv_manager = None
29
-
30
- # --- Frontend Serving Route ---
31
- @app.route('/')
32
- def index():
33
- """Serves the main chat application page (index.html)."""
34
- return render_template('index.html')
35
-
36
- # --- API Endpoints ---
37
-
38
- @app.route('/config', methods=['GET'])
39
- def get_config():
40
- """Provides public configuration keys to the frontend."""
41
- return jsonify({
42
- 'imgbb_api_key': os.getenv('IMGBB_API_KEY')
43
- })
44
-
45
- @app.route('/chat', methods=['POST'])
46
- def chat():
47
- """Handles incoming user messages and returns the assistant's response."""
48
- if not assistant:
49
- return jsonify({"error": "Assistant is not available due to an initialization error."}), 503
50
-
51
- # The request is multipart/form-data to handle potential image uploads
52
- data = request.form
53
- user_message = data.get('message')
54
- session_id = data.get('session_id')
55
- image_url = data.get('image_url') # The permanent URL from ImgBB
56
-
57
- # A message must contain either text or an image
58
- if not user_message and not image_url:
59
- return jsonify({"error": "Cannot process an empty message."}), 400
60
-
61
- # If no session_id is provided by the client, it's a new conversation
62
- if not session_id or session_id == 'null' or session_id == 'undefined':
63
- session_id = str(uuid.uuid4())
64
- logger.info(f"No session_id provided. Creating new session: {session_id}")
65
-
66
- # Call the assistant's core logic, now passing the image_url separately
67
- response_content = assistant.process_query(
68
- user_message=user_message or "", # Ensure user_message is a string
69
- session_id=session_id,
70
- image_url=image_url
71
- )
72
-
73
- return jsonify({
74
- "response": response_content,
75
- "session_id": session_id
76
- })
77
-
78
- @app.route('/history/sessions', methods=['GET'])
79
- def get_sessions():
80
- """Fetches a list of all conversation sessions for the sidebar."""
81
- if not conv_manager:
82
- return jsonify({"error": "Conversation manager not available"}), 503
83
- try:
84
- # Fetch session_id and the full history to generate a title
85
- all_conversations = conv_manager.supabase.table('conversations').select('session_id, history').execute()
86
-
87
- sessions = []
88
- for conv in all_conversations.data:
89
- title = "New Chat" # Default title
90
- # Generate a title from the first user message in the history
91
- if conv.get('history') and len(conv['history']) > 0:
92
- first_message_content = conv['history'][0].get('content')
93
- if first_message_content:
94
- # Truncate for display
95
- title = first_message_content[:35] + '...' if len(first_message_content) > 35 else first_message_content
96
- else: # If the first message was only an image
97
- title = "Image Query"
98
-
99
- sessions.append({
100
- "session_id": conv.get('session_id'),
101
- "title": title
102
- })
103
-
104
- return jsonify(sessions)
105
- except Exception as e:
106
- logger.error(f"Error fetching sessions from Supabase: {e}")
107
- return jsonify({"error": "Could not fetch conversation sessions"}), 500
108
-
109
- @app.route('/history/messages/<session_id>', methods=['GET'])
110
- def get_messages(session_id):
111
- """Fetches the full, structured message history for a given session_id."""
112
- if not conv_manager:
113
- return jsonify({"error": "Conversation manager not available"}), 503
114
- try:
115
- history = conv_manager.get_history(session_id)
116
- return jsonify(history)
117
- except Exception as e:
118
- logger.error(f"Error fetching messages for session {session_id}: {e}")
119
- return jsonify({"error": "Could not fetch message history"}), 500
120
-
121
- @app.route('/clear', methods=['POST'])
122
- def clear_history():
123
- """Deletes a conversation history from the database."""
124
- if not assistant:
125
- return jsonify({"error": "Assistant is not available."}), 503
126
-
127
- data = request.get_json()
128
- session_id = data.get('session_id')
129
-
130
- if not session_id:
131
- return jsonify({"error": "Missing 'session_id' in request body"}), 400
132
-
133
- if assistant.clear_history(session_id):
134
- return jsonify({"status": "success", "message": f"History for session {session_id} was cleared."})
135
- else:
136
- return jsonify({"error": "Failed to clear history."}), 500
137
-
138
- # --- Main Execution ---
139
- if __name__ == '__main__':
140
- # Set debug=False for production
141
- app.run(debug=False, port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # server.py
2
+
3
+ from flask import Flask, request, jsonify, render_template
4
+ from app import EasyFarmsAssistant
5
+ from conversation_manager import ConversationManager
6
+ import logging
7
+ import os
8
+ from flask_cors import CORS
9
+
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Initialize the Flask application
15
+ app = Flask(__name__)
16
+ CORS(app)
17
+
18
+ # --- Initialize Core Services ---
19
+ try:
20
+ conv_manager = ConversationManager()
21
+ assistant = EasyFarmsAssistant(manager=conv_manager)
22
+ logger.info("EasyFarmsAssistant and ConversationManager initialized successfully.")
23
+ except Exception as e:
24
+ logger.error(f"FATAL: Could not initialize services. Error: {e}")
25
+ assistant = None
26
+ conv_manager = None
27
+
28
+ # --- Frontend Serving Route ---
29
+ @app.route('/')
30
+ def index():
31
+ """Serves the main chat application page (index.html)."""
32
+ return render_template('index.html')
33
+
34
+ # --- API Endpoints ---
35
+
36
+ @app.route('/config', methods=['GET'])
37
+ def get_config():
38
+ """Provides public configuration keys to the frontend."""
39
+ return jsonify({
40
+ 'imgbb_api_key': os.getenv('IMGBB_API_KEY')
41
+ })
42
+
43
+ @app.route('/chat', methods=['POST'])
44
+ def chat():
45
+ """
46
+ Handles incoming user messages and returns the assistant's response.
47
+
48
+ Request format:
49
+ {
50
+ "query": "user message text",
51
+ "image_url": "optional image URL",
52
+ "session_id": "optional chat ID for conversation memory"
53
+ }
54
+
55
+ Response format:
56
+ {
57
+ "response": "assistant response",
58
+ "chat_id": "unique chat identifier",
59
+ "is_new_chat": true/false,
60
+ "user_message_id": 1,
61
+ "assistant_message_id": 2,
62
+ "total_messages": 2
63
+ }
64
+ """
65
+ if not assistant:
66
+ return jsonify({"error": "Assistant is not available due to an initialization error."}), 503
67
+
68
+ try:
69
+ # Handle both JSON and form data
70
+ if request.is_json:
71
+ data = request.get_json()
72
+ user_message = data.get('query')
73
+ chat_id = data.get('session_id')
74
+ image_url = data.get('image_url')
75
+ else:
76
+ # Fallback for form data (backward compatibility)
77
+ data = request.form
78
+ user_message = data.get('message') or data.get('query')
79
+ chat_id = data.get('session_id')
80
+ image_url = data.get('image_url')
81
+
82
+ # Validate input
83
+ if not user_message and not image_url:
84
+ return jsonify({"error": "Cannot process an empty message. Provide either 'query' text or 'image_url'."}), 400
85
+
86
+ # Clean up empty/null chat_id
87
+ if chat_id in [None, '', 'null', 'undefined']:
88
+ chat_id = None
89
+
90
+ # Process the query with the assistant
91
+ result = assistant.process_query(
92
+ user_message=user_message or "",
93
+ chat_id=chat_id,
94
+ image_url=image_url
95
+ )
96
+
97
+ # Handle errors from assistant
98
+ if "error" in result:
99
+ return jsonify({
100
+ "error": result["error"],
101
+ "chat_id": result.get("chat_id"),
102
+ "is_new_chat": result.get("is_new_chat", True)
103
+ }), 500
104
+
105
+ # Return successful response
106
+ return jsonify({
107
+ "response": result["response"],
108
+ "chat_id": result["chat_id"],
109
+ "is_new_chat": result["is_new_chat"],
110
+ "user_message_id": result["user_message_id"],
111
+ "assistant_message_id": result["assistant_message_id"],
112
+ "total_messages": result["total_messages"]
113
+ })
114
+
115
+ except Exception as e:
116
+ logger.error(f"Error in chat endpoint: {e}")
117
+ return jsonify({"error": f"Server error: {str(e)}"}), 500
118
+
119
+ @app.route('/chats', methods=['GET'])
120
+ def get_all_chats():
121
+ """
122
+ Get all chat sessions with basic information.
123
+
124
+ Response format:
125
+ [
126
+ {
127
+ "session_id": "chat_abc123",
128
+ "title": "Crop recommendations for...",
129
+ "message_count": 4,
130
+ "created_at": "2024-01-01T10:00:00",
131
+ "updated_at": "2024-01-01T10:05:00"
132
+ }
133
+ ]
134
+ """
135
+ if not conv_manager:
136
+ return jsonify({"error": "Conversation manager not available"}), 503
137
+
138
+ try:
139
+ chats = assistant.get_all_chats()
140
+ return jsonify(chats)
141
+
142
+ except Exception as e:
143
+ logger.error(f"Error fetching all chats: {e}")
144
+ return jsonify({"error": "Could not fetch chat list"}), 500
145
+
146
+ @app.route('/chat/<chat_id>/info', methods=['GET'])
147
+ def get_chat_info(chat_id):
148
+ """
149
+ Get information about a specific chat.
150
+
151
+ Response format:
152
+ {
153
+ "session_id": "chat_abc123",
154
+ "message_count": 4,
155
+ "created_at": "2024-01-01T10:00:00",
156
+ "updated_at": "2024-01-01T10:05:00",
157
+ "exists": true
158
+ }
159
+ """
160
+ if not assistant:
161
+ return jsonify({"error": "Assistant not available"}), 503
162
+
163
+ try:
164
+ if not chat_id:
165
+ return jsonify({"error": "Chat ID is required"}), 400
166
+
167
+ info = assistant.get_chat_info(chat_id)
168
+ return jsonify(info)
169
+
170
+ except Exception as e:
171
+ logger.error(f"Error getting chat info for {chat_id}: {e}")
172
+ return jsonify({"error": "Could not fetch chat information"}), 500
173
+
174
+ @app.route('/chat/<chat_id>/messages', methods=['GET'])
175
+ def get_chat_messages(chat_id):
176
+ """
177
+ Get all messages for a specific chat.
178
+
179
+ Response format:
180
+ [
181
+ {
182
+ "message_id": 1,
183
+ "role": "user",
184
+ "content": "Hello, I need help with farming",
185
+ "timestamp": "2024-01-01T10:00:00",
186
+ "imageUrl": "optional"
187
+ },
188
+ {
189
+ "message_id": 2,
190
+ "role": "assistant",
191
+ "content": "Hello! I'd be happy to help...",
192
+ "timestamp": "2024-01-01T10:00:05"
193
+ }
194
+ ]
195
+ """
196
+ if not assistant:
197
+ return jsonify({"error": "Assistant not available"}), 503
198
+
199
+ try:
200
+ if not chat_id:
201
+ return jsonify({"error": "Chat ID is required"}), 400
202
+
203
+ messages = assistant.get_messages(chat_id)
204
+ return jsonify(messages)
205
+
206
+ except Exception as e:
207
+ logger.error(f"Error fetching messages for chat {chat_id}: {e}")
208
+ return jsonify({"error": "Could not fetch chat messages"}), 500
209
+
210
+ @app.route('/chat/<chat_id>', methods=['DELETE'])
211
+ def delete_chat(chat_id):
212
+ """
213
+ Delete a specific chat and all its messages.
214
+
215
+ Response format:
216
+ {
217
+ "status": "success",
218
+ "message": "Chat deleted successfully",
219
+ "chat_id": "chat_abc123"
220
+ }
221
+ """
222
+ if not assistant:
223
+ return jsonify({"error": "Assistant not available"}), 503
224
+
225
+ try:
226
+ if not chat_id:
227
+ return jsonify({"error": "Chat ID is required"}), 400
228
+
229
+ success = assistant.clear_history(chat_id)
230
+
231
+ if success:
232
+ return jsonify({
233
+ "status": "success",
234
+ "message": f"Chat {chat_id} deleted successfully",
235
+ "chat_id": chat_id
236
+ })
237
+ else:
238
+ return jsonify({
239
+ "status": "error",
240
+ "message": f"Failed to delete chat {chat_id}",
241
+ "chat_id": chat_id
242
+ }), 500
243
+
244
+ except Exception as e:
245
+ logger.error(f"Error deleting chat {chat_id}: {e}")
246
+ return jsonify({"error": "Could not delete chat"}), 500
247
+
248
+ # --- Legacy Endpoints for Backward Compatibility ---
249
+
250
+ @app.route('/history/sessions', methods=['GET'])
251
+ def get_sessions():
252
+ """Legacy endpoint - redirects to /chats"""
253
+ return get_all_chats()
254
+
255
+ @app.route('/history/messages/<session_id>', methods=['GET'])
256
+ def get_messages(session_id):
257
+ """Legacy endpoint - redirects to /chat/<session_id>/messages"""
258
+ return get_chat_messages(session_id)
259
+
260
+ @app.route('/clear', methods=['POST'])
261
+ def clear_history():
262
+ """Legacy endpoint for clearing chat history"""
263
+ if not assistant:
264
+ return jsonify({"error": "Assistant is not available."}), 503
265
+
266
+ try:
267
+ data = request.get_json()
268
+ if not data:
269
+ return jsonify({"error": "Request body is required"}), 400
270
+
271
+ session_id = data.get('session_id')
272
+ if not session_id:
273
+ return jsonify({"error": "Missing 'session_id' in request body"}), 400
274
+
275
+ success = assistant.clear_history(session_id)
276
+
277
+ if success:
278
+ return jsonify({
279
+ "status": "success",
280
+ "message": f"History for session {session_id} was cleared."
281
+ })
282
+ else:
283
+ return jsonify({"error": "Failed to clear history."}), 500
284
+
285
+ except Exception as e:
286
+ logger.error(f"Error in legacy clear endpoint: {e}")
287
+ return jsonify({"error": f"Server error: {str(e)}"}), 500
288
+
289
+ # --- Health Check and Status Endpoints ---
290
+
291
+ @app.route('/health', methods=['GET'])
292
+ def health_check():
293
+ """Health check endpoint for monitoring"""
294
+ try:
295
+ # Check if core services are available
296
+ services_status = {
297
+ "assistant": assistant is not None,
298
+ "conversation_manager": conv_manager is not None,
299
+ "database": False
300
+ }
301
+
302
+ # Test database connectivity
303
+ if conv_manager:
304
+ try:
305
+ # Try to get chat sessions (will test DB connection)
306
+ conv_manager.get_all_chat_sessions()
307
+ services_status["database"] = True
308
+ except:
309
+ services_status["database"] = False
310
+
311
+ all_healthy = all(services_status.values())
312
+
313
+ return jsonify({
314
+ "status": "healthy" if all_healthy else "degraded",
315
+ "services": services_status,
316
+ "timestamp": conv_manager.manager._get_timestamp() if conv_manager else None
317
+ }), 200 if all_healthy else 503
318
+
319
+ except Exception as e:
320
+ logger.error(f"Health check failed: {e}")
321
+ return jsonify({
322
+ "status": "unhealthy",
323
+ "error": str(e)
324
+ }), 503
325
+
326
+ @app.route('/stats', methods=['GET'])
327
+ def get_stats():
328
+ """Get system statistics"""
329
+ if not conv_manager:
330
+ return jsonify({"error": "Conversation manager not available"}), 503
331
+
332
+ try:
333
+ chats = assistant.get_all_chats()
334
+
335
+ total_chats = len(chats)
336
+ total_messages = sum(chat.get('message_count', 0) for chat in chats)
337
+
338
+ # Recent activity (chats updated in last 24 hours)
339
+ from datetime import datetime, timedelta
340
+ now = datetime.utcnow()
341
+ yesterday = now - timedelta(days=1)
342
+
343
+ recent_chats = 0
344
+ for chat in chats:
345
+ updated_at = chat.get('updated_at')
346
+ if updated_at:
347
+ try:
348
+ chat_time = datetime.fromisoformat(updated_at.replace('Z', '+00:00'))
349
+ if chat_time > yesterday:
350
+ recent_chats += 1
351
+ except:
352
+ pass
353
+
354
+ return jsonify({
355
+ "total_chats": total_chats,
356
+ "total_messages": total_messages,
357
+ "recent_chats_24h": recent_chats,
358
+ "average_messages_per_chat": round(total_messages / total_chats, 2) if total_chats > 0 else 0
359
+ })
360
+
361
+ except Exception as e:
362
+ logger.error(f"Error getting stats: {e}")
363
+ return jsonify({"error": "Could not fetch statistics"}), 500
364
+
365
+ # --- Error Handlers ---
366
+
367
+ @app.errorhandler(404)
368
+ def not_found(error):
369
+ """Handle 404 errors"""
370
+ return jsonify({"error": "Endpoint not found"}), 404
371
+
372
+ @app.errorhandler(500)
373
+ def internal_error(error):
374
+ """Handle 500 errors"""
375
+ logger.error(f"Internal server error: {error}")
376
+ return jsonify({"error": "Internal server error"}), 500
377
+
378
+ # --- Main Execution ---
379
+ if __name__ == '__main__':
380
+ # Set debug=False for production
381
+ port = int(os.environ.get('PORT', 7860))
382
+ app.run(debug=False, port=port, host='0.0.0.0')