roshnn24 commited on
Commit
22c23cc
·
verified ·
1 Parent(s): 7678bdf

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +609 -0
app.py ADDED
@@ -0,0 +1,609 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import subprocess
3
+ import tempfile
4
+ import os
5
+ from langchain.llms import Ollama
6
+ from langchain.prompts import PromptTemplate
7
+ from langchain.chains import LLMChain
8
+ from langchain.memory import ConversationBufferMemory
9
+ from datetime import datetime
10
+ import json
11
+ from typing import Dict, List
12
+ import sqlite3
13
+ from contextlib import contextmanager
14
+ import re
15
+ import os
16
+ from werkzeug.utils import secure_filename
17
+
18
+ app = Flask(__name__)
19
+
20
+ UPLOAD_FOLDER = 'uploads'
21
+ ALLOWED_EXTENSIONS = {'py'}
22
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
23
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
24
+ # Database configuration
25
+ DATABASE_PATH = 'chat_database.db'
26
+
27
+ # Initialize LangChain with Ollama LLM
28
+ llm = Ollama(model="mistral:7b")
29
+
30
+
31
+ @contextmanager
32
+ def get_db_connection():
33
+ conn = sqlite3.connect(DATABASE_PATH)
34
+ conn.row_factory = sqlite3.Row
35
+ try:
36
+ yield conn
37
+ finally:
38
+ conn.close()
39
+
40
+
41
+ def init_db():
42
+ with get_db_connection() as conn:
43
+ conn.execute('''
44
+ CREATE TABLE IF NOT EXISTS chats (
45
+ id TEXT PRIMARY KEY,
46
+ title TEXT,
47
+ date TEXT,
48
+ last_message TEXT
49
+ )
50
+ ''')
51
+
52
+ conn.execute('''
53
+ CREATE TABLE IF NOT EXISTS messages (
54
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
55
+ chat_id TEXT,
56
+ role TEXT,
57
+ content TEXT,
58
+ timestamp TEXT,
59
+ FOREIGN KEY (chat_id) REFERENCES chats (id)
60
+ )
61
+ ''')
62
+
63
+ conn.execute('''
64
+ CREATE TABLE IF NOT EXISTS important_info (
65
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
66
+ chat_id TEXT,
67
+ content TEXT,
68
+ FOREIGN KEY (chat_id) REFERENCES chats (id)
69
+ )
70
+ ''')
71
+ conn.commit()
72
+
73
+
74
+ # Initialize database on startup
75
+ init_db()
76
+
77
+
78
+ class ChatSession:
79
+ def __init__(self, session_id):
80
+ self.session_id = session_id
81
+ self.memory = ConversationBufferMemory(
82
+ memory_key="chat_history",
83
+ return_messages=True
84
+ )
85
+ self._load_chat_history()
86
+ self._load_important_info()
87
+
88
+ def _load_chat_history(self):
89
+ """Load chat history from database"""
90
+ with get_db_connection() as conn:
91
+ messages = conn.execute(
92
+ 'SELECT role, content, timestamp FROM messages WHERE chat_id = ? ORDER BY timestamp',
93
+ (self.session_id,)
94
+ ).fetchall()
95
+
96
+ self.chat_history = []
97
+ for msg in messages:
98
+ self.chat_history.append({
99
+ "role": msg['role'],
100
+ "content": msg['content'],
101
+ "timestamp": msg['timestamp']
102
+ })
103
+ if msg['role'] == "user":
104
+ self.memory.chat_memory.add_user_message(msg['content'])
105
+ else:
106
+ self.memory.chat_memory.add_ai_message(msg['content'])
107
+
108
+ def _load_important_info(self):
109
+ """Load important info from database"""
110
+ with get_db_connection() as conn:
111
+ info = conn.execute(
112
+ 'SELECT content FROM important_info WHERE chat_id = ?',
113
+ (self.session_id,)
114
+ ).fetchall()
115
+ self.important_info = [row['content'] for row in info]
116
+
117
+ def add_message(self, role, content):
118
+ timestamp = datetime.now().isoformat()
119
+ message = {
120
+ "role": role,
121
+ "content": content,
122
+ "timestamp": timestamp
123
+ }
124
+
125
+ # Store in database
126
+ with get_db_connection() as conn:
127
+ conn.execute(
128
+ 'INSERT INTO messages (chat_id, role, content, timestamp) VALUES (?, ?, ?, ?)',
129
+ (self.session_id, role, content, timestamp)
130
+ )
131
+ conn.commit()
132
+
133
+ self.chat_history.append(message)
134
+
135
+ # Update memory
136
+ if role == "user":
137
+ self.memory.chat_memory.add_user_message(content)
138
+ else:
139
+ self.memory.chat_memory.add_ai_message(content)
140
+
141
+ def add_important_info(self, content):
142
+ """Add important information to database"""
143
+ with get_db_connection() as conn:
144
+ conn.execute(
145
+ 'INSERT INTO important_info (chat_id, content) VALUES (?, ?)',
146
+ (self.session_id, content)
147
+ )
148
+ conn.commit()
149
+ self.important_info.append(content)
150
+
151
+ def get_memory_variables(self):
152
+ return self.memory.load_memory_variables({})
153
+
154
+ def clear_memory(self):
155
+ """Clear all memory from database"""
156
+ with get_db_connection() as conn:
157
+ conn.execute('DELETE FROM messages WHERE chat_id = ?', (self.session_id,))
158
+ conn.execute('DELETE FROM important_info WHERE chat_id = ?', (self.session_id,))
159
+ conn.commit()
160
+
161
+ self.memory.clear()
162
+ self.chat_history = []
163
+ self.important_info = []
164
+
165
+ def clear_chat_history(self):
166
+ """Clear chat history from database"""
167
+ with get_db_connection() as conn:
168
+ conn.execute('DELETE FROM messages WHERE chat_id = ?', (self.session_id,))
169
+ conn.commit()
170
+
171
+ self.chat_history = []
172
+ self.memory.chat_memory.clear()
173
+
174
+ def clear_important_info(self):
175
+ """Clear important info from database"""
176
+ with get_db_connection() as conn:
177
+ conn.execute('DELETE FROM important_info WHERE chat_id = ?', (self.session_id,))
178
+ conn.commit()
179
+
180
+ self.important_info = []
181
+
182
+
183
+ # Rest of the prompt template and other configurations remain the same
184
+ prompt_template = """
185
+ Role: You are Figr Code Assistant, specializing in providing clear, error-free Python code solutions.
186
+
187
+ Context:
188
+ {important_info}
189
+
190
+ Previous Conversation:
191
+ {chat_history}
192
+
193
+ Current Request:
194
+ {user_request}
195
+
196
+ Output Guidelines:
197
+ 1. Code Format:
198
+ - Use ```python for code blocks
199
+ - Use `code` for inline code references
200
+ - Provide raw text without HTML formatting
201
+ - Strictly include explanation only after code blocks
202
+
203
+ 2. Code Organization:
204
+ - Default to single, focused code snippets for clarity
205
+ - Only split into multiple snippets(each individually runnable) if:
206
+ a) Multiple distinct concepts are requested
207
+ b) Complex functionality requires modular explanation
208
+
209
+ - Mark critical information with [IMPORTANT] prefix and give small explanations with some bold headings if required and in white font always.
210
+ """
211
+
212
+ prompt = PromptTemplate(
213
+ input_variables=["user_request", "chat_history", "important_info"],
214
+ template=prompt_template
215
+ )
216
+ llm_chain = LLMChain(llm=llm, prompt=prompt)
217
+
218
+
219
+ def convert_to_html(raw_text):
220
+ """Convert markdown to HTML while preserving code blocks with custom buttons"""
221
+ try:
222
+ # Create a temporary markdown file
223
+ with tempfile.NamedTemporaryFile(delete=False, mode="w", suffix=".md") as temp_input:
224
+ temp_input.write(raw_text)
225
+ temp_input_path = temp_input.name
226
+
227
+ # Use pandoc with specific options to preserve code blocks
228
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as temp_output:
229
+ temp_output_path = temp_output.name
230
+
231
+ # Use pandoc with specific options
232
+ cmd = [
233
+ "pandoc",
234
+ temp_input_path,
235
+ "-f", "markdown",
236
+ "-t", "html",
237
+ "--highlight-style=pygments",
238
+ "--no-highlight", # Disable pandoc's highlighting
239
+ "-o", temp_output_path
240
+ ]
241
+
242
+ result = subprocess.run(cmd, capture_output=True, text=True)
243
+
244
+ if result.returncode == 0:
245
+ with open(temp_output_path, "r") as f:
246
+ html_content = f.read()
247
+
248
+ # Add custom buttons to code blocks
249
+ import re
250
+ def replace_code_block(match):
251
+ code_class = match.group(1) or ''
252
+ code_content = match.group(2)
253
+ return f'''
254
+ <div class="code-block-wrapper">
255
+ <button class="test-button">Test Code</button>
256
+ <button class="copy-button">Copy Code</button>
257
+ <pre><code class="hljs {code_class}">{code_content}</code></pre>
258
+ <div class="test-results"></div>
259
+ </div>
260
+ '''
261
+
262
+ # Replace <pre><code> blocks with our custom wrapper
263
+ pattern = r'<pre><code class="([^"]*)">(.*?)</code></pre>'
264
+ html_content = re.sub(pattern, replace_code_block, html_content, flags=re.DOTALL)
265
+
266
+ else:
267
+ html_content = f"Error: {result.stderr}"
268
+
269
+ finally:
270
+ # Clean up temporary files
271
+ if os.path.exists(temp_input_path):
272
+ os.remove(temp_input_path)
273
+ if os.path.exists(temp_output_path):
274
+ os.remove(temp_output_path)
275
+
276
+ return html_content
277
+
278
+
279
+ def allowed_file(filename):
280
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
281
+
282
+
283
+ def extract_important_info(response):
284
+ important_items = []
285
+ for line in response.split('\n'):
286
+ if '[IMPORTANT]' in line:
287
+ important_items.append(line.replace('[IMPORTANT]', '').strip())
288
+ return important_items
289
+
290
+ def create_new_chat(session_id: str):
291
+ """Create a new chat session with metadata in database"""
292
+ with get_db_connection() as conn:
293
+ conn.execute(
294
+ 'INSERT INTO chats (id, title, date, last_message) VALUES (?, ?, ?, ?)',
295
+ (session_id, "New Chat", datetime.now().isoformat(), None)
296
+ )
297
+ conn.commit()
298
+
299
+ return {
300
+ "id": session_id,
301
+ "title": "New Chat",
302
+ "date": datetime.now().isoformat(),
303
+ "last_message": None
304
+ }
305
+
306
+
307
+ def update_chat_metadata(session_id: str, last_message: str):
308
+ """Update chat metadata in database"""
309
+ title = last_message[:30] + "..." if len(last_message) > 30 else last_message
310
+ with get_db_connection() as conn:
311
+ conn.execute(
312
+ 'UPDATE chats SET title = ?, last_message = ? WHERE id = ?',
313
+ (title, last_message, session_id)
314
+ )
315
+ conn.commit()
316
+
317
+
318
+ def format_response(response):
319
+ """Format response with proper code block structure"""
320
+ # First, handle code blocks with language specification
321
+ formatted = re.sub(
322
+ r'```(\w+)\n(.*?)\n```',
323
+ lambda
324
+ m: f'<div class="code-block-wrapper">\n<button class="test-button">Test Code</button>\n<button class="copy-button">Copy Code</button>\n<pre><code class="hljs {m.group(1)}">{m.group(2)}</code></pre>\n<div class="test-results"></div>\n</div>',
325
+ response,
326
+ flags=re.DOTALL
327
+ )
328
+
329
+ # Then handle code blocks without language specification
330
+ formatted = re.sub(
331
+ r'```\n(.*?)\n```',
332
+ lambda
333
+ m: f'<div class="code-block-wrapper">\n<button class="test-button">Test Code</button>\n<button class="copy-button">Copy Code</button>\n<pre><code class="hljs">{m.group(1)}</code></pre>\n<div class="test-results"></div>\n</div>',
334
+ formatted,
335
+ flags=re.DOTALL
336
+ )
337
+
338
+ # Handle inline code
339
+ formatted = re.sub(
340
+ r'`([^`]+)`',
341
+ r'<code class="inline-code">\1</code>',
342
+ formatted
343
+ )
344
+
345
+ return formatted
346
+
347
+
348
+ @app.route("/api/chat-list", methods=["GET"])
349
+ def get_chat_list():
350
+ """Get list of all chats from database"""
351
+ with get_db_connection() as conn:
352
+ chats = conn.execute('SELECT * FROM chats ORDER BY date DESC').fetchall()
353
+ return jsonify({
354
+ "chats": [dict(chat) for chat in chats]
355
+ })
356
+
357
+
358
+ # The rest of your route handlers (convert_to_html, extract_important_info, etc.) remain the same
359
+
360
+ @app.route("/api/chat", methods=["POST"])
361
+ def chat():
362
+ data = request.json
363
+ user_input = data.get("message", "")
364
+ session_id = data.get("sessionId", "default")
365
+
366
+ # Get or create session
367
+ session = ChatSession(session_id)
368
+
369
+ try:
370
+ # Add user message
371
+ session.add_message("user", user_input)
372
+ update_chat_metadata(session_id, user_input)
373
+
374
+ # Get memory variables
375
+ memory_vars = session.get_memory_variables()
376
+
377
+ # Generate response
378
+ raw_response = llm_chain.run(
379
+ user_request=user_input,
380
+ chat_history=memory_vars.get("chat_history", ""),
381
+ important_info="\n".join(session.important_info)
382
+ )
383
+
384
+ # Extract and store important information
385
+ new_important_info = extract_important_info(raw_response)
386
+ for info in new_important_info:
387
+ session.add_important_info(info)
388
+
389
+ # Format the response properly with code block structure
390
+ def format_response(response):
391
+ # First, handle code blocks with language specification
392
+ formatted = re.sub(
393
+ r'```(\w+)\n(.*?)\n```',
394
+ lambda
395
+ m: f'<div class="code-block-wrapper">\n<button class="test-button">Test Code</button>\n<button class="copy-button">Copy Code</button>\n<pre><code class="hljs {m.group(1)}">{m.group(2)}</code></pre>\n<div class="test-results"></div>\n</div>',
396
+ response,
397
+ flags=re.DOTALL
398
+ )
399
+
400
+ # Then handle code blocks without language specification
401
+ formatted = re.sub(
402
+ r'```\n(.*?)\n```',
403
+ lambda
404
+ m: f'<div class="code-block-wrapper">\n<button class="test-button">Test Code</button>\n<button class="copy-button">Copy Code</button>\n<pre><code class="hljs">{m.group(1)}</code></pre>\n<div class="test-results"></div>\n</div>',
405
+ formatted,
406
+ flags=re.DOTALL
407
+ )
408
+
409
+ # Handle inline code
410
+ formatted = re.sub(
411
+ r'`([^`]+)`',
412
+ r'<code class="inline-code">\1</code>',
413
+ formatted
414
+ )
415
+
416
+ return formatted
417
+
418
+ # Format the response
419
+ formatted_response = format_response(raw_response)
420
+
421
+ # Store the formatted response
422
+ session.add_message("assistant", formatted_response)
423
+
424
+ return jsonify({
425
+ "response": formatted_response,
426
+ "success": True,
427
+ "important_info": session.important_info
428
+ })
429
+
430
+ except Exception as e:
431
+ return jsonify({
432
+ "response": f"An error occurred: {str(e)}",
433
+ "success": False
434
+ })
435
+
436
+
437
+ @app.route("/api/new-chat", methods=["POST"])
438
+ def new_chat():
439
+ """Create a new chat session"""
440
+ session_id = str(datetime.now().timestamp())
441
+ chat = create_new_chat(session_id)
442
+ return jsonify({"success": True, "chat": chat})
443
+
444
+
445
+ @app.route("/api/chat-history", methods=["GET"])
446
+ def get_chat_history():
447
+ """Get chat history for a specific session"""
448
+ session_id = request.args.get("sessionId", "default")
449
+
450
+ with get_db_connection() as conn:
451
+ # Get messages
452
+ messages = conn.execute(
453
+ 'SELECT role, content, timestamp FROM messages WHERE chat_id = ? ORDER BY timestamp',
454
+ (session_id,)
455
+ ).fetchall()
456
+
457
+ # Format assistant messages if they aren't already formatted
458
+ formatted_messages = []
459
+ for msg in messages:
460
+ message_dict = dict(msg)
461
+ if message_dict['role'] == 'assistant' and '```' in message_dict['content']:
462
+ # Format the response if it contains code blocks
463
+ message_dict['content'] = format_response(message_dict['content'])
464
+ formatted_messages.append(message_dict)
465
+
466
+ # Get important info
467
+ important_info = conn.execute(
468
+ 'SELECT content FROM important_info WHERE chat_id = ?',
469
+ (session_id,)
470
+ ).fetchall()
471
+
472
+ return jsonify({
473
+ "history": formatted_messages,
474
+ "important_info": [info['content'] for info in important_info]
475
+ })
476
+
477
+
478
+ @app.route('/api/upload', methods=['POST'])
479
+ def upload_file():
480
+ if 'file' not in request.files:
481
+ return jsonify({'success': False, 'error': 'No file part'})
482
+
483
+ file = request.files['file']
484
+ if file.filename == '':
485
+ return jsonify({'success': False, 'error': 'No selected file'})
486
+
487
+ if file and allowed_file(file.filename):
488
+ filename = secure_filename(file.filename)
489
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
490
+ file.save(filepath)
491
+
492
+ # Read the file content
493
+ with open(filepath, 'r') as f:
494
+ content = f.read()
495
+
496
+ # Analyze the code using the LLM
497
+ analysis_prompt = f"""
498
+ Please analyze this Python code:
499
+
500
+ {content}
501
+
502
+ Provide:
503
+ 1. A clear, small explanation
504
+ 2. Any potential errors or improvements
505
+ 3. Suggestions for better practices
506
+
507
+ - Each in separate neat paragraphs with highlighted headings.
508
+ """
509
+
510
+ analysis = llm.predict(analysis_prompt)
511
+
512
+ # Clean up the uploaded file
513
+ os.remove(filepath)
514
+
515
+ return jsonify({
516
+ 'success': True,
517
+ 'filename': filename,
518
+ 'content': content,
519
+ 'analysis': analysis
520
+ })
521
+
522
+ return jsonify({'success': False, 'error': 'Invalid file type'})
523
+
524
+
525
+ @app.route("/api/clear-memory", methods=["POST"])
526
+ def clear_memory():
527
+ """Clear memory based on specified option"""
528
+ session_id = request.json.get("sessionId", "default")
529
+ clear_option = request.json.get("clearOption", "all")
530
+
531
+ with get_db_connection() as conn:
532
+ try:
533
+ if clear_option == "all":
534
+ conn.execute('DELETE FROM messages WHERE chat_id = ?', (session_id,))
535
+ conn.execute('DELETE FROM important_info WHERE chat_id = ?', (session_id,))
536
+ message = "All memory cleared successfully"
537
+ elif clear_option == "chat":
538
+ conn.execute('DELETE FROM messages WHERE chat_id = ?', (session_id,))
539
+ message = "Chat history cleared successfully"
540
+ elif clear_option == "important":
541
+ conn.execute('DELETE FROM important_info WHERE chat_id = ?', (session_id,))
542
+ message = "Important information cleared successfully"
543
+ else:
544
+ return jsonify({
545
+ "success": False,
546
+ "message": "Invalid clear option specified"
547
+ })
548
+
549
+ conn.commit()
550
+ return jsonify({
551
+ "success": True,
552
+ "message": message
553
+ })
554
+
555
+ except Exception as e:
556
+ return jsonify({
557
+ "success": False,
558
+ "message": f"Error clearing memory: {str(e)}"
559
+ })
560
+
561
+
562
+ @app.route("/api/test-code", methods=["POST"])
563
+ def test_code():
564
+ try:
565
+ data = request.json
566
+ code = data.get("code", "")
567
+
568
+ # Create a temporary file to store the code
569
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
570
+ f.write(code)
571
+ temp_file = f.name
572
+
573
+ try:
574
+ # Run the code using Python
575
+ result = subprocess.run(
576
+ ['python', temp_file],
577
+ capture_output=True,
578
+ text=True,
579
+ timeout=5 # 5 second timeout for safety
580
+ )
581
+
582
+ # Prepare the response
583
+ success = result.returncode == 0
584
+ output = result.stdout if success else result.stderr
585
+
586
+ return jsonify({
587
+ "success": success,
588
+ "output": output
589
+ })
590
+
591
+ finally:
592
+ # Clean up the temporary file
593
+ os.unlink(temp_file)
594
+
595
+ except Exception as e:
596
+ return jsonify({
597
+ "success": False,
598
+ "output": f"Error executing code: {str(e)}"
599
+ })
600
+
601
+
602
+ @app.route("/")
603
+ def home():
604
+ """Serve the main application page"""
605
+ return render_template("index.html")
606
+
607
+
608
+ if __name__ == "__main__":
609
+ app.run(debug=True)