kltn21110 commited on
Commit
6d0dfcd
·
verified ·
1 Parent(s): 33251a9

Update function/chat.py

Browse files
Files changed (1) hide show
  1. function/chat.py +863 -860
function/chat.py CHANGED
@@ -1,860 +1,863 @@
1
- from langchain_community.utilities.sql_database import SQLDatabase
2
- from langchain_experimental.sql import SQLDatabaseChain
3
- import sys
4
- import os
5
- import pymysql
6
- from fastapi import HTTPException
7
- from fastapi.encoders import jsonable_encoder
8
- import re
9
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
10
- import prompt.prompt_main as prompt
11
- import prompt.prompt_custom as prompt_cus
12
- os.environ["GOOGLE_API_KEY"] = "AIzaSyBoPdVLTJNxfDg9wxWDpY4QJezHiyjKbTE"
13
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
14
- from bson import ObjectId
15
- import os
16
- from dotenv import load_dotenv
17
- import os
18
- from dotenv import load_dotenv
19
- from dotenv import load_dotenv, find_dotenv
20
- load_dotenv(find_dotenv(), override=True)
21
-
22
-
23
-
24
- DB_HOST = os.getenv("DB_HOST")
25
- DB_USER = os.getenv("DB_USER")
26
- DB_PASSWORD = os.getenv("DB_PASSWORD")
27
- DB_NAME = os.getenv("DB_NAME")
28
- DB_PORT = os.getenv("DB_PORT")
29
- # Tạo connection string
30
- import os
31
- from urllib.parse import quote
32
-
33
- password = os.getenv("DB_PASSWORD") # VD: 'Yahana0509@'
34
- DB_PASSWORD = quote(password)
35
- connection_uri = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
36
- db = SQLDatabase.from_uri(connection_uri)
37
- from dotenv import load_dotenv
38
- import filter.filter_role as filter_role_1
39
- import filter.filter_sql_injection as filter_sql_injection_1
40
- import filter.result as query_result_1
41
- import support.get_key as get_key
42
- import response.ResponseChat as res_chat
43
- from datetime import datetime
44
- import pytz
45
- from mongoengine import connect
46
- import sys
47
- import os
48
- import nltk
49
- import function.agent.pipeline_agent as pipeline_agent
50
- nltk.download('punkt')
51
- from models.Database_Entity import User, ChatHistory, DetailChat
52
- from dotenv import load_dotenv
53
- load_dotenv()
54
- MONGO_URI = os.getenv("MONGO_URI", "")
55
- connect("chatbot_hmdrinks", host=MONGO_URI)
56
-
57
- load_dotenv()
58
-
59
- #setup model
60
- from bson import ObjectId
61
- import random
62
- from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
63
-
64
-
65
-
66
- BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
67
- sys.path.insert(0, BASE_DIR)
68
- from repository.MySQL import UserRepository
69
- from prompt.prompt_syntax_insert import is_insert_related_to_product_category_variant, filter_syntax_sql
70
- import sqlparse
71
- import re
72
- import sys
73
- import os
74
- sys.path.append(os.path.abspath(os.path.join(os.getcwd(), '..')))
75
- from prompt import prompt_detail_table
76
-
77
- schema_mapping = {
78
- "user": prompt_detail_table.prompt_users,
79
- "user_voucher": prompt_detail_table.prompt_user_voucher,
80
- "category": prompt_detail_table.prompt_categort,
81
- "category_translation": prompt_detail_table.prompt_category_translation,
82
- "cart": prompt_detail_table.prompt_cart,
83
- "cart_item": prompt_detail_table.prompt_cart_item,
84
- "orders":prompt_detail_table.prompt_orders,
85
- "order_item": prompt_detail_table.prompt_order_item,
86
- "payment": prompt_detail_table.prompt_payments,
87
- "payments": prompt_detail_table.prompt_payments,
88
- "favourite": prompt_detail_table.prompt_favourite,
89
- "favourite_item": prompt_detail_table.prompt_fav_item,
90
- "post": prompt_detail_table.prompt_post,
91
- "post_translation": prompt_detail_table.prompt_post_translation,
92
- "product": prompt_detail_table.prompt_product,
93
- "product_translation": prompt_detail_table.prompt_product_translation,
94
- "shipment": prompt_detail_table.prompt_shipment,
95
- "product_variants": prompt_detail_table.prompt_product_variants,
96
- "review": prompt_detail_table.prompt_review,
97
- "user_coin": prompt_detail_table.prompt_user_coin,
98
- "absence": prompt_detail_table.prompt_absence,
99
- "cart_group": prompt_detail_table.prompt_cart_group,
100
- "cart_item_group": prompt_detail_table.prompt_cartitem_group,
101
- "group_orders": prompt_detail_table.prompt_group_orders,
102
- "payments_group": prompt_detail_table.prompt_payments_group,
103
- "group_order_members":prompt_detail_table.prompt_group_orders_member,
104
- "shipment_group":prompt_detail_table.prompt_shipment_group,
105
- "shipper_attendance":prompt_detail_table.prompt_shipper_attendance,
106
- "shipper_commission_detail":prompt_detail_table.prompt_shipper_commission_detail,
107
- "shipper_salary_summary":prompt_detail_table.prompt_shipper_salary_summary,
108
- "voucher": prompt_detail_table.prompt_voucher
109
- }
110
-
111
-
112
- def get_schemas_from_sql(sql_query: str, schema_mapping: dict):
113
- import sqlglot
114
-
115
- parsed_query = sqlglot.parse_one(sql_query, read="mysql")
116
-
117
- # Lấy danh sách bảng duy nhất trong query
118
- table_names = list({t.name for t in parsed_query.find_all(sqlglot.exp.Table)})
119
-
120
- schemas_used = {}
121
- for table in table_names:
122
- if table in schema_mapping:
123
- schemas_used[table] = schema_mapping[table]
124
- else:
125
- print(f"⚠️ Warning: Table '{table}' not found in schema_mapping")
126
-
127
- # Gom toàn bộ schema thành 1 chuỗi duy nhất
128
- all_schemas = "\n\n".join(
129
- [f"Schema for table '{table}':\n{schemas_used[table]}" for table in schemas_used]
130
- )
131
-
132
- return all_schemas
133
-
134
-
135
- def build_sql_fix_prompt(schemas_result: dict, sql: str) -> str:
136
-
137
- prompt = f"""
138
- Bạn một chuyên gia sở dữ liệu.
139
-
140
- Dưới đây mô tả schema chi tiết của các bảng có trong hệ thống:
141
- Đọc ghi nhớ tùng thuộc tính của mỗi bảng mà bạn truy vấn:
142
- {schemas_result}
143
-
144
- ---
145
-
146
- Dưới đây là một câu SQL đang bị lỗi do không đúng tên bảng hoặc tên cột:
147
-
148
- ```sql
149
- {sql.strip()}
150
- Yêu cầu của bạn là:
151
-
152
- Dựa trên các schema ở trên, hãy kiểm tra và chỉnh sửa câu SQL sao cho:
153
- Tên bảng, tên cột(thuộc tính) phải chính xác theo trình bày bên trong schema.
154
- Nếu tên cột trong bảng đó có mô tả trong schema mà trình bày khác thì phải thay đổi sao cho cú pháp sql chính xác.
155
- Logic mục đích của truy vấn được giữ nguyên.
156
- Chỉ trả lại phần SQL đã được chỉnh sửa (không giải thích, không chú thích, không thêm nhận xét).
157
- Trả lời dưới dạng một truy vấn SQL duy nhất.
158
- Không dùng pt.`language_ code` = 'EN
159
- Với các câu hỏi liên quan đến product luôn trả về kèm theo pro_id cho mình.
160
- - "- Tránh các lỗi như :\n"
161
- " (1054, \"Unknown column 'oi.pro_id' in 'field list'\")\n . Luôn đảm bảo bạn không bao giờ bị lỗi này"
162
- " (1054, \"Unknown column 'oi.note' in 'field list'\") . Luôn đảm bảo bạn không bao giờ bị lỗi này\n"
163
- " (1054, \"Unknown column 'oi.size' in 'field list'\") . Luôn đảm bảo bạn không bao giờ bị lỗi này \n"
164
- " (1054, \"Unknown column 'c.is_deleted' in 'on clause'\"). Luôn đảm bảo bạn không bao giờ bị lỗi này\n"
165
- """.strip()
166
- return prompt
167
-
168
-
169
- def contains_delete(sql: str) -> bool:
170
- return bool(re.search(r'\bdelete\b', sql, re.IGNORECASE))
171
-
172
- async def execute_query_user(user_input: str, user_id: int, languages: str, role: str):
173
- api_key = get_key.get_random_api_key()
174
- os.environ["GOOGLE_API_KEY"] = api_key
175
- llm1 = ChatGoogleGenerativeAI(model='gemini-2.0-flash-thinking-exp-01-21',temperature=0.2)
176
- db = SQLDatabase.from_uri(connection_uri)
177
- PROMPT_CUSTOM = await prompt_cus.get_prompt_custom(user_input)
178
- check_insert = is_insert_related_to_product_category_variant(user_input)
179
-
180
- db_config = {
181
- "host": os.getenv("DB_HOST"),
182
- "user": os.getenv("DB_USER"),
183
- "database": os.getenv("DB_NAME"),
184
- "password": os.getenv("DB_PASSWORD"),
185
- "port": int(os.getenv("DB_PORT", 3306)),
186
- "charset": "utf8mb4",
187
- "cursorclass": pymysql.cursors.DictCursor,
188
- }
189
-
190
- def regenerate_sql_until_safe():
191
- max_retry = 5
192
- retry_count = 0
193
-
194
- while retry_count < max_retry:
195
- try:
196
- regenerated_data = db_chain.run(f"""
197
- Role: {text_role}
198
- Language: {languages}
199
- Question: {user_input}.
200
- """)
201
- regenerated_sql = extract_sql_from_response(regenerated_data)
202
- if regenerated_sql:
203
- regenerated_sql = clean_sql(regenerated_sql)
204
- if not re.search(r"%{1,2}s", regenerated_sql): # đã sạch
205
- return regenerated_sql
206
- retry_count += 1
207
- except Exception as e:
208
- return f"❌ Lỗi khi tạo lại truy vấn lần {retry_count + 1}: {str(e)}"
209
- return "❌ Lỗi: Không thể tạo được truy vấn an toàn sau nhiều lần thử."
210
- def execute_query_with_pymysql(query, multi=False):
211
- connection = pymysql.connect(**db_config)
212
- try:
213
- with connection.cursor() as cursor:
214
- results = []
215
- if multi:
216
- statements = sqlparse.split(query)
217
- for stmt in statements:
218
- stmt = stmt.strip()
219
- if stmt:
220
- try:
221
- cursor.execute(stmt)
222
- try:
223
- results.append(cursor.fetchall())
224
- except pymysql.ProgrammingError:
225
- results.append("✅ Executed")
226
- except Exception as e:
227
- results.append(f"❌ Error in query: {stmt}\n{str(e)}")
228
- else:
229
- try:
230
- cursor.execute(query)
231
- results = cursor.fetchall()
232
- except Exception as e:
233
- return f"❌ Error executing query: {str(e)}"
234
- connection.commit()
235
- return results
236
- except pymysql.MySQLError as e:
237
- return f"❌ MySQL Error: {str(e)}"
238
- finally:
239
- connection.close()
240
-
241
- def clean_sql(sql) -> str:
242
- if isinstance(sql, dict) and sql:
243
- first_value = next(iter(sql.values()))
244
- sql = first_value
245
- sql = re.sub(r"```sql", "", sql, flags=re.IGNORECASE)
246
- sql = sql.replace("```sql", "")
247
- sql = re.sub(r"```", "", sql)
248
- return sql.strip()
249
-
250
- def extract_sql_from_response(data):
251
- # Định dạng markdown block [SQL: ```sql ... ```]
252
- match = re.search(r"\[SQL:\s*```sql\s*(.*?)\s*```]", data, re.DOTALL)
253
- if match:
254
- return clean_sql(match.group(1))
255
-
256
- # Định dạng đơn giản SQLQuery: ...
257
- match = re.search(r"SQLQuery:\s*(.*)", data, re.DOTALL)
258
- if match:
259
- return clean_sql(match.group(1))
260
-
261
- return None
262
-
263
- def extract_sql_from_error(error_msg):
264
- # Case 1: [SQL: ```sql ... ```]
265
- match = re.search(r"\[SQL:\s*```sql\s*(.*?)\s*```]", error_msg, re.DOTALL)
266
- if match:
267
- return clean_sql(match.group(1))
268
- match = re.search(r"```(?:sql)?\s*\r?\n(.*?)```", error_msg, re.DOTALL)
269
- if match:
270
- return clean_sql(match.group(1))
271
-
272
- return None
273
-
274
- def process_and_execute_sql(sql):
275
- data = sql
276
- if isinstance(data, dict) and data:
277
- first_key, first_value = next(iter(data.items()))
278
- sql = first_value
279
- elif isinstance(data, list) and data:
280
- sql = "\n\n".join(data)
281
- sql_clean = clean_sql(sql)
282
- if re.search(r"%{1,2}s", sql_clean):
283
- regenerated_sql = regenerate_sql_until_safe()
284
- sql_clean = clean_sql(regenerated_sql)
285
- result = get_schemas_from_sql(sql_clean, schema_mapping)
286
- print("result",result)
287
- prompt = build_sql_fix_prompt(result,sql =sql_clean)
288
- from advance_shopping.call_gemini import tool_call
289
- data = tool_call.generate(prompt = prompt)
290
- sql_clean = clean_sql(data)
291
- print("SQL step2: ", sql_clean)
292
- if sql_clean == None:
293
- return "❌ Lỗi không thể thực thi truy vấn: Không tìm thấy SQL trong phản hồi."
294
- if contains_delete(sql_clean):
295
- return "Lỗi: Bạn không dược phép thực hiện truy vấn DELETE trong hệ thống này."
296
- if re.search(r"\\bIF\\b.*\\bTHEN\\b", sql_clean, re.IGNORECASE):
297
- return "❌ Lỗi: Không được dùng IF...THEN trong SQL. Vui lòng chia nhỏ truy vấn."
298
-
299
- if check_insert:
300
- check_syntax = filter_syntax_sql(sql_clean, PROMPT_CUSTOM, user_input)
301
-
302
- if check_syntax is True:
303
- try:
304
- connection = pymysql.connect(**db_config)
305
- with connection.cursor() as cursor:
306
- statements = sqlparse.split(sql_clean)
307
- results = []
308
- for stmt in statements:
309
- stmt = stmt.strip()
310
- if stmt:
311
- try:
312
- cursor.execute(stmt)
313
- try:
314
- results.append(cursor.fetchall())
315
- except:
316
- results.append("✅ OK")
317
- except Exception as e:
318
- return f"❌ Lỗi tại truy vấn: `{stmt}`\nChi tiết: {str(e)}"
319
- connection.commit()
320
- return results
321
- except Exception as e:
322
- return f"❌ Lỗi khi thực thi từng truy vấn: {str(e)}"
323
- finally:
324
- connection.close()
325
- else:
326
- try:
327
- regenerated_data = db_chain.run(f"""
328
- Role: {text_role}
329
- Language: {languages}
330
- Question: {user_input}.
331
- """)
332
- regenerated_sql = extract_sql_from_response(regenerated_data)
333
- if regenerated_sql:
334
- return process_and_execute_sql(regenerated_sql)
335
- else:
336
- return "❌ Lỗi: Không thể tạo lại truy vấn hợp lệ."
337
- except Exception as regen_error:
338
- return f"❌ Lỗi khi tạo lại truy vấn: {str(regen_error)}"
339
- else:
340
- return execute_query_with_pymysql(sql_clean, multi=True)
341
-
342
- if "Do not use IF...THEN" not in PROMPT_CUSTOM.template:
343
- PROMPT_CUSTOM.template += (
344
- "\n\n⚠️ Note: Do NOT use IF...THEN...ELSE...END in SQL. "
345
- "Only use plain SELECT, INSERT, UPDATE, DELETE, SET statements."
346
- )
347
-
348
- db_chain = SQLDatabaseChain.from_llm(llm=llm1, db=db, prompt=PROMPT_CUSTOM)
349
- text_role = f"{role} (userId = {user_id})" if role == "ADMIN" else f"{role} (userId = {user_id}), not role ADMIN"
350
- try:
351
- data = db_chain.run(f"""
352
- Role: {text_role}
353
- Language: {languages}
354
- Question: {user_input}.
355
- """)
356
- extracted_sql = extract_sql_from_response(data)
357
- if extracted_sql:
358
- print("SQL:",extracted_sql)
359
- print(extracted_sql)
360
- return process_and_execute_sql(extracted_sql)
361
- else:
362
- return data
363
-
364
- except Exception as e:
365
- error_message = str(e)
366
- print("Lỗi: ",error_message)
367
- extracted_sql = extract_sql_from_error(error_message)
368
- print("SQL lỗi: ",extracted_sql)
369
- if extracted_sql == None:
370
- return "❌ Lỗi không thể thực thi truy vấn: Không tìm thấy SQL trong phản hồi."
371
- fix_sql = re.sub(r"```sql", "", extracted_sql, flags=re.IGNORECASE)
372
- fix_sql = re.sub(r"```", "", fix_sql)
373
- fix_sql = re.sub(r'%%s', r'%s', fix_sql)
374
- print(fix_sql)
375
- print("SQL fix:",fix_sql)
376
- if contains_delete(fix_sql):
377
- return "Lỗi: Bạn không dược phép thực hiện truy vấn DELETE trong hệ thống này."
378
- if extracted_sql:
379
- return process_and_execute_sql(fix_sql)
380
- else:
381
- return f"❌ Lỗi không thể thực thi truy vấn: {error_message}"
382
-
383
-
384
- async def create_new_chat_history(user_id: int) -> str:
385
- """
386
- Tạo một đoạn chat mới cho user_id và trả về ObjectId của đoạn chat.
387
- """
388
- check = UserRepository.getUserByUserId(user_id)
389
- if check is None:
390
- raise HTTPException(status_code=404, detail="User not found or has been deleted in MySQL")
391
-
392
- user = User.objects(user_id=user_id).first()
393
- if not user:
394
- user = User(id=ObjectId(), user_id=user_id, user_name=f"User_{user_id}")
395
- user.save()
396
- random_name_chat = str(random.randint(10**14, 10**15 - 1))
397
- name_chat = f"Chat_{random_name_chat}"
398
- new_chat = ChatHistory(id=ObjectId(), user=user, name_chat=name_chat)
399
- new_chat.save()
400
-
401
- return res_chat.CreateNewChat(idMongo=str(new_chat.id), chat_name=name_chat)
402
-
403
-
404
- async def update_chat_name(chat_id: str, new_name: str,user_id:int) -> str:
405
- """
406
- Cập nhật name_chat của một ChatHistory dựa trên chat_id.
407
- """
408
- check = UserRepository.getUserByUserId(user_id)
409
- if check is None:
410
- raise HTTPException(status_code=404, detail="User not found or has been deleted in MySQL")
411
-
412
- user = User.objects(user_id=user_id, is_deleted=False).first()
413
- if not user:
414
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
415
-
416
- chat = ChatHistory.objects(_id=ObjectId(chat_id)).first()
417
-
418
- if not chat:
419
- raise HTTPException(status_code=404, detail="Chat not found in MongoDB")
420
-
421
- chat.name_chat = new_name
422
- chat.save()
423
-
424
- return f"Updated chat name to {new_name}"
425
-
426
-
427
- async def soft_delete_chat(chat_id: str,user_id:int):
428
- """
429
- Cập nhật `is_deleted=True` và `date_deleted` cho `ChatHistory` và các `DetailChat` liên quan.
430
- """
431
- check = UserRepository.getUserByUserId(user_id)
432
- if check is None:
433
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
434
-
435
- check_history_id = UserRepository.getChatHistory(user_id,chat_id)
436
- if check_history_id is None:
437
- raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MySQL")
438
-
439
- user = User.objects(user_id=user_id, is_deleted=False).first()
440
- if not user:
441
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
442
-
443
- chat = ChatHistory.objects(_id=ObjectId(chat_id)).first()
444
-
445
- if not chat:
446
- raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MongoDB")
447
- chat.is_deleted = True
448
- chat.date_deleted = datetime.now(pytz.UTC)
449
- chat.save()
450
-
451
- DetailChat.objects(chat_history=chat).update(
452
- set__is_deleted=True,
453
- set__date_deleted=datetime.now(pytz.UTC)
454
- )
455
-
456
- return {"message": "Chat and related details marked as deleted"}
457
-
458
-
459
- from datetime import datetime, timedelta
460
- async def chat_with_user(
461
- user_input: str,
462
- user_id: int,
463
- languages: str,
464
- role: str,
465
- token: str,
466
- chat_history_id: str = None,
467
-
468
- ) -> str:
469
- """
470
- Xử lý tin nhắn của người dùng, lưu vào lịch sử chat và trả về phản hồi từ AI.
471
- """
472
-
473
- if role not in ["ADMIN", "CUSTOMER", "SHIPPER"]:
474
- raise HTTPException(status_code=400, detail="ROLE not valid")
475
- user_id = int(user_id)
476
- if languages not in ["VN", "EN"]:
477
- raise HTTPException(status_code=400, detail="Language not valid")
478
-
479
- if not user_input:
480
- raise HTTPException(status_code=400, detail="User input empty")
481
- if not isinstance(user_id, int) or user_id <= 0:
482
- raise HTTPException(status_code=400, detail="Invalid user_id: must be a positive integer")
483
-
484
- languages = "Vietnamese" if languages == "VN" else "English"
485
-
486
- check = UserRepository.getUserByUserId(user_id)
487
- if check is None:
488
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
489
-
490
- check_history_id = UserRepository.getChatHistory(user_id,chat_history_id)
491
- if check_history_id is None:
492
- raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MySQL")
493
-
494
- user = User.objects(user_id=user_id).first()
495
- if not user:
496
- return {"error": "User not found or has been deleted in MongoDB"}
497
-
498
- chat_history = None
499
- if chat_history_id:
500
- try:
501
- chat_history_obj_id = ObjectId(chat_history_id)
502
- chat_history = ChatHistory.objects(_id=chat_history_obj_id, user=user).first()
503
- except Exception as e:
504
-
505
- print(f"⚠️ Invalid chat_history_id: {e}")
506
- if not chat_history:
507
- raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MongoDB")
508
- # filtered_input = filter_sql_injection_1.filter_sql_injection(user_input)
509
- # filtered_role_input = filter_role_1.filter_role(filtered_input)
510
- # result = await execute_query_user(filtered_role_input, user_id, languages, role)
511
- # result_final = query_result_1.query_result(user_input, result)
512
- result_final = await pipeline_agent.multi_query_user(user_input,user_id,role,languages,chat_history_id, token)
513
-
514
- detail_chat = DetailChat(
515
- id=ObjectId(),
516
- chat_history=chat_history,
517
- you_message=user_input,
518
- ai_message=result_final,
519
- timestamp=datetime.now(pytz.UTC)
520
- )
521
- detail_chat.save()
522
- chat_history_messages = DetailChat.objects(chat_history=chat_history).order_by('timestamp')
523
-
524
-
525
- def convert_to_vn_time(timestamp):
526
- return (timestamp + timedelta(hours=7)).strftime('%Y-%m-%dT%H:%M:%S')
527
-
528
- sorted_messages = sorted(chat_history_messages, key=lambda msg: msg.timestamp, reverse=True)
529
-
530
- formatted_messages = [
531
- {
532
- "index": i + 1, # Đánh số từ 1
533
- "id": str(msg.id),
534
- "you_message": msg.you_message,
535
- "ai_message": msg.ai_message,
536
- "timestamp": convert_to_vn_time(msg.timestamp)
537
- }
538
- for i, msg in enumerate(sorted_messages)
539
- ]
540
-
541
- return jsonable_encoder({
542
- "new_message": {
543
- "id": str(detail_chat.id),
544
- "you_message": detail_chat.you_message,
545
- "ai_message": detail_chat.ai_message,
546
- "timestamp": convert_to_vn_time(detail_chat.timestamp)
547
- },
548
- "previous_messages": formatted_messages
549
- })
550
-
551
- from bson import ObjectId
552
-
553
-
554
- async def get_chat_details(chat_id: str,user_id:int):
555
- """
556
- Lấy tất cả `DetailChat` thuộc `ChatHistory` có `chat_id`, chỉ lấy bản ghi `is_deleted=False`.
557
- """
558
-
559
- check = UserRepository.getUserByUserId(user_id)
560
- if check is None:
561
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
562
-
563
- check_history_id = UserRepository.getChatHistory(user_id,chat_id)
564
- if check_history_id is None:
565
- raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MySQL")
566
-
567
- user = User.objects(user_id=user_id, is_deleted=False).first()
568
- if not user:
569
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
570
-
571
- chat = ChatHistory.objects(_id=ObjectId(chat_id), is_deleted=False).first()
572
-
573
- if not chat:
574
- raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MongoDB")
575
-
576
- chat_details = DetailChat.objects(chat_history=chat, is_deleted=False)
577
- def convert_to_vn_time(timestamp):
578
- return (timestamp + timedelta(hours=7)).strftime('%Y-%m-%dT%H:%M:%S')
579
-
580
- list_detail_response = [
581
- res_chat.DetailResponse(
582
- id=str(index + 1), # ✅ Đánh số lại từ 1
583
- you_message=detail.you_message,
584
- ai_message=detail.ai_message,
585
- timestamp=convert_to_vn_time(detail.timestamp) # ✅ Chuyển sang GMT+7
586
- )
587
- for index, detail in enumerate(sorted(chat_details, key=lambda d: d.timestamp, reverse=True)) # ✅ Sắp xếp giảm dần
588
- ]
589
-
590
- return res_chat.ListDetailResponse(
591
- chat_id=str(chat.id),
592
- chat_name=chat.name_chat,
593
- list_detail_response=list_detail_response
594
- )
595
-
596
-
597
-
598
- async def regenerate(
599
- user_question_new: str,
600
- user_id: int,
601
- languages: str,
602
- role: str,
603
- chat_history_id: str = None
604
- ) -> str:
605
- """
606
- Xử tin nhắn của người dùng, lưu vào lịch sử chat và trả về phản hồi từ AI.
607
- """
608
- PROMPT_CUSTOM = await prompt_cus.get_prompt_custom(user_question_new)
609
- if role not in ["ADMIN", "CUSTOMER", "SHIPPER"]:
610
- raise HTTPException(status_code=400, detail="ROLE not valid")
611
- user_id = int(user_id)
612
- if languages not in ["VN", "EN"]:
613
- raise HTTPException(status_code=400, detail="Language not valid")
614
-
615
- if not user_question_new:
616
- raise HTTPException(status_code=400, detail="User input empty")
617
- if not isinstance(user_id, int) or user_id <= 0:
618
- raise HTTPException(status_code=400, detail="Invalid user_id: must be a positive integer")
619
-
620
- languages = "Vietnamese" if languages == "VN" else "English"
621
-
622
- check = UserRepository.getUserByUserId(user_id)
623
- if check is None:
624
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
625
-
626
- check_history_id = UserRepository.getChatHistory(user_id,chat_history_id)
627
- if check_history_id is None:
628
- raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MySQL")
629
-
630
- user = User.objects(user_id=user_id).first()
631
- if not user:
632
- return {"error": "User not found or has been deleted in MongoDB"}
633
-
634
- chat_history = None
635
- if chat_history_id:
636
- try:
637
- chat_history_obj_id = ObjectId(chat_history_id) # Chuyển đổi sang ObjectId
638
- chat_history = ChatHistory.objects(_id=chat_history_obj_id, user=user).first()
639
- except Exception as e:
640
-
641
- print(f"⚠️ Invalid chat_history_id: {e}")
642
- if not chat_history:
643
- raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MongoDB")
644
- # filtered_input = filter_sql_injection_1.filter_sql_injection(user_input)
645
- # filtered_role_input = filter_role_1.filter_role(filtered_input)
646
- # result = await execute_query_user(filtered_role_input, user_id, languages, role)
647
- # result_final = query_result_1.query_result(user_input, result)
648
- if chat_history:
649
- last_chat = (
650
- DetailChat.objects(chat_history=chat_history, is_deleted=False)
651
- .order_by("-timestamp")
652
- .first()
653
- )
654
-
655
- # if last_chat:
656
- # print(f"Last chat - You: {last_chat.you_message}, AI: {last_chat.ai_message}")
657
- # else:
658
- # print("⚠️ No chat details found for this history.")
659
- result_final = await pipeline_agent.multi_query_user(user_question_new,user_id,role,languages,chat_history_id)
660
- last_chat.update(set__you_message=user_question_new, set__ai_message=result_final, set__timestamp = datetime.now(pytz.UTC))
661
-
662
- last_chat_result = (
663
- DetailChat.objects(chat_history=chat_history, is_deleted=False)
664
- .order_by("-timestamp")
665
- .first()
666
- )
667
-
668
- # detail_chat = DetailChat(
669
- # id=ObjectId(),
670
- # chat_history=chat_history,
671
- # you_message=user_input,
672
- # ai_message=result_final,
673
- # timestamp=datetime.now(pytz.UTC)
674
- # )
675
- # detail_chat.save()
676
- chat_history_messages = DetailChat.objects(chat_history=chat_history).order_by('timestamp')
677
-
678
-
679
- def convert_to_vn_time(timestamp):
680
- return (timestamp + timedelta(hours=7)).strftime('%Y-%m-%dT%H:%M:%S')
681
-
682
- sorted_messages = sorted(chat_history_messages, key=lambda msg: msg.timestamp, reverse=True)
683
-
684
- formatted_messages = [
685
- {
686
- "index": i + 1, # Đánh số từ 1
687
- "id": str(msg.id),
688
- "you_message": msg.you_message,
689
- "ai_message": msg.ai_message,
690
- "timestamp": convert_to_vn_time(msg.timestamp)
691
- }
692
- for i, msg in enumerate(sorted_messages)
693
- ]
694
-
695
- return jsonable_encoder({
696
- "new_message": {
697
- "id": str(last_chat_result.id),
698
- "you_message": last_chat_result.you_message,
699
- "ai_message": last_chat_result.ai_message,
700
- "timestamp": convert_to_vn_time(last_chat_result.timestamp)
701
- },
702
- "previous_messages": formatted_messages
703
- })
704
-
705
- from bson import ObjectId
706
-
707
-
708
- async def get_user_chat_history(user_id: int):
709
- """
710
- API lấy danh sách tất cả các đoạn chat của một user_id.
711
- """
712
- check = UserRepository.getUserByUserId(user_id)
713
- if check is None:
714
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
715
- user = User.objects(user_id=user_id, is_deleted=False).first()
716
- if not user:
717
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
718
- chat_histories = ChatHistory.objects(user=user, is_deleted=False)
719
- chat_list = [
720
- res_chat.ChatResponse(
721
- chat_id=str(chat.id),
722
- chat_name=chat.name_chat,
723
- timestamp=chat.date_deleted if chat.is_deleted else chat.id.generation_time
724
- )
725
- for chat in chat_histories
726
- ]
727
-
728
- return res_chat.UserChatHistoryResponse(
729
- user_id=user.user_id,
730
- user_name=user.user_name,
731
- chat_list=chat_list
732
- )
733
-
734
-
735
- from bson import ObjectId
736
-
737
- async def get_chat_details_text(chat_id: str, user_id: int):
738
- """
739
- Trích xuất tất cả các chi tiết chat của một chat_id, gom thành một đoạn văn bản.
740
- """
741
- # Kiểm tra xem user có tồn tại không
742
- user = User.objects(user_id=user_id, is_deleted=False).first()
743
- if not user:
744
- raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
745
-
746
- # Kiểm tra xem chat history có tồn tại không
747
- chat = ChatHistory.objects(_id=ObjectId(chat_id), user=user, is_deleted=False).first()
748
- if not chat:
749
- raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MongoDB")
750
-
751
- # Lấy tất cả các chi tiết chat liên quan
752
- chat_details = DetailChat.objects(chat_history=chat, is_deleted=False).order_by('timestamp')
753
-
754
- if not chat_details:
755
- return list()
756
-
757
- # Gom tất cả các câu hỏi và câu trả lời vào danh sách
758
- chat_text_list = []
759
- for index, detail in enumerate(chat_details,start=1):
760
- chat_text_list.append({
761
- "order":str(index),
762
- "timestamp": detail.timestamp.strftime("%Y-%m-%d %H:%M:%S"),
763
- "you_message": detail.you_message,
764
- "ai_message": detail.ai_message
765
- })
766
-
767
- return chat_text_list
768
-
769
-
770
- import asyncio
771
- import os
772
- import subprocess
773
- from datetime import datetime
774
- from pathlib import Path
775
- from function.analyze import main
776
- import asyncio
777
- from models.Database_Entity import StopSignal
778
- from function.analyze.gemini import result_analyze
779
- async def check_should_stop(chat_id: str, stop_event: object = None):
780
- # Trường hợp dừng qua RAM (in-memory)
781
- await asyncio.sleep(0.1)
782
- if stop_event and stop_event.is_set():
783
- print("🛑 Dừng qua stop_event.")
784
- return {"status": "cancelled"}
785
-
786
- # Trường hợp dừng qua MongoDB
787
- await asyncio.sleep(0.1)
788
- if StopSignal.objects(chat_history=chat_id, is_stopped=True).first():
789
- print("🛑 Dừng StopSignal trong DB.")
790
- return {"status": "cancelled"}
791
-
792
- return None
793
-
794
- from typing import Optional
795
- async def generate_and_save_code(question: str, user_id: int, role, languages: str,stop_event: Optional[asyncio.Event], filename: str = "analyze_result.py",chat_id: str = ""):
796
- result_check = await check_should_stop(chat_id, stop_event)
797
- if result_check:
798
- return result_check
799
- code_test, folder_name = await main.analyze(
800
- question,
801
- user_id,
802
- languages,
803
- role,
804
- chat_id,
805
- stop_event
806
- )
807
- result_check = await check_should_stop(chat_id, stop_event)
808
- if result_check:
809
- return result_check
810
- code_clean = code_test.strip()
811
- if code_clean.startswith("```python"):
812
- code_clean = code_clean[9:].strip()
813
- if code_clean.endswith("```"):
814
- code_clean = code_clean[:-3].strip()
815
- result_check = await check_should_stop(chat_id, stop_event)
816
- if result_check:
817
- return result_check
818
- # code_clean = code_clean.replace("else:", "").strip()
819
- code_clean = code_clean.replace("os_path:", "os.path").strip()
820
- encoding_fix = 'import sys\nsys.stdout.reconfigure(encoding="utf-8")\n\n'
821
- encoding_fix1= 'import numpy as np\n\n'
822
- code_clean = encoding_fix + encoding_fix1 + code_clean
823
-
824
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
825
- output_dir = Path(f"./Temp/{folder_name}_{timestamp}")
826
- output_dir.mkdir(parents=True, exist_ok=True)
827
-
828
- file_path = output_dir / filename
829
- with open(file_path, "w", encoding="utf-8") as f:
830
- f.write(code_clean)
831
-
832
-
833
- env = os.environ.copy()
834
- result_check = await check_should_stop(chat_id, stop_event)
835
- if result_check:
836
- return result_check
837
- env["OUTPUT_DIR"] = str(output_dir)
838
- result = subprocess.run(
839
- ["python", filename],
840
- capture_output=True,
841
- text=True,
842
- env=env,
843
- cwd=output_dir,
844
- encoding="utf-8",
845
- errors="replace"
846
- )
847
- result_check = await check_should_stop(chat_id, stop_event)
848
- if result_check:
849
- return result_check
850
- output_folder = str(output_dir)
851
- absolute_path = os.path.abspath(output_folder)
852
- final_path = os.path.join(absolute_path, "test5")
853
- result_check = await check_should_stop(chat_id, stop_event)
854
- if result_check:
855
- return result_check
856
- result_final = result_analyze.generate(image_folder=final_path,question=question)
857
- result_check = await check_should_stop(chat_id, stop_event)
858
- if result_check:
859
- return result_check
860
- return result_final, final_path
 
 
 
 
1
+ from langchain_community.utilities.sql_database import SQLDatabase
2
+ from langchain_experimental.sql import SQLDatabaseChain
3
+ import sys
4
+ import os
5
+ import pymysql
6
+ from fastapi import HTTPException
7
+ from fastapi.encoders import jsonable_encoder
8
+ import re
9
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
10
+ import prompt.prompt_main as prompt
11
+ import prompt.prompt_custom as prompt_cus
12
+ os.environ["GOOGLE_API_KEY"] = "AIzaSyBoPdVLTJNxfDg9wxWDpY4QJezHiyjKbTE"
13
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
14
+ from bson import ObjectId
15
+ import os
16
+ from dotenv import load_dotenv
17
+ import os
18
+ from dotenv import load_dotenv
19
+ from dotenv import load_dotenv, find_dotenv
20
+ load_dotenv(find_dotenv(), override=True)
21
+
22
+
23
+
24
+ DB_HOST = os.getenv("DB_HOST")
25
+ DB_USER = os.getenv("DB_USER")
26
+ DB_PASSWORD = os.getenv("DB_PASSWORD")
27
+ DB_NAME = os.getenv("DB_NAME")
28
+ DB_PORT = os.getenv("DB_PORT")
29
+ # Tạo connection string
30
+ import os
31
+ from urllib.parse import quote
32
+
33
+ password = os.getenv("DB_PASSWORD") # VD: 'Yahana0509@'
34
+ DB_PASSWORD = quote(password)
35
+ connection_uri = (
36
+ f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
37
+ "?ssl_verify_cert=false&ssl_verify_identity=false"
38
+ )
39
+ db = SQLDatabase.from_uri(connection_uri)
40
+ from dotenv import load_dotenv
41
+ import filter.filter_role as filter_role_1
42
+ import filter.filter_sql_injection as filter_sql_injection_1
43
+ import filter.result as query_result_1
44
+ import support.get_key as get_key
45
+ import response.ResponseChat as res_chat
46
+ from datetime import datetime
47
+ import pytz
48
+ from mongoengine import connect
49
+ import sys
50
+ import os
51
+ import nltk
52
+ import function.agent.pipeline_agent as pipeline_agent
53
+ nltk.download('punkt')
54
+ from models.Database_Entity import User, ChatHistory, DetailChat
55
+ from dotenv import load_dotenv
56
+ load_dotenv()
57
+ MONGO_URI = os.getenv("MONGO_URI", "")
58
+ connect("chatbot_hmdrinks", host=MONGO_URI)
59
+
60
+ load_dotenv()
61
+
62
+ #setup model
63
+ from bson import ObjectId
64
+ import random
65
+ from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
66
+
67
+
68
+
69
+ BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
70
+ sys.path.insert(0, BASE_DIR)
71
+ from repository.MySQL import UserRepository
72
+ from prompt.prompt_syntax_insert import is_insert_related_to_product_category_variant, filter_syntax_sql
73
+ import sqlparse
74
+ import re
75
+ import sys
76
+ import os
77
+ sys.path.append(os.path.abspath(os.path.join(os.getcwd(), '..')))
78
+ from prompt import prompt_detail_table
79
+
80
+ schema_mapping = {
81
+ "user": prompt_detail_table.prompt_users,
82
+ "user_voucher": prompt_detail_table.prompt_user_voucher,
83
+ "category": prompt_detail_table.prompt_categort,
84
+ "category_translation": prompt_detail_table.prompt_category_translation,
85
+ "cart": prompt_detail_table.prompt_cart,
86
+ "cart_item": prompt_detail_table.prompt_cart_item,
87
+ "orders":prompt_detail_table.prompt_orders,
88
+ "order_item": prompt_detail_table.prompt_order_item,
89
+ "payment": prompt_detail_table.prompt_payments,
90
+ "payments": prompt_detail_table.prompt_payments,
91
+ "favourite": prompt_detail_table.prompt_favourite,
92
+ "favourite_item": prompt_detail_table.prompt_fav_item,
93
+ "post": prompt_detail_table.prompt_post,
94
+ "post_translation": prompt_detail_table.prompt_post_translation,
95
+ "product": prompt_detail_table.prompt_product,
96
+ "product_translation": prompt_detail_table.prompt_product_translation,
97
+ "shipment": prompt_detail_table.prompt_shipment,
98
+ "product_variants": prompt_detail_table.prompt_product_variants,
99
+ "review": prompt_detail_table.prompt_review,
100
+ "user_coin": prompt_detail_table.prompt_user_coin,
101
+ "absence": prompt_detail_table.prompt_absence,
102
+ "cart_group": prompt_detail_table.prompt_cart_group,
103
+ "cart_item_group": prompt_detail_table.prompt_cartitem_group,
104
+ "group_orders": prompt_detail_table.prompt_group_orders,
105
+ "payments_group": prompt_detail_table.prompt_payments_group,
106
+ "group_order_members":prompt_detail_table.prompt_group_orders_member,
107
+ "shipment_group":prompt_detail_table.prompt_shipment_group,
108
+ "shipper_attendance":prompt_detail_table.prompt_shipper_attendance,
109
+ "shipper_commission_detail":prompt_detail_table.prompt_shipper_commission_detail,
110
+ "shipper_salary_summary":prompt_detail_table.prompt_shipper_salary_summary,
111
+ "voucher": prompt_detail_table.prompt_voucher
112
+ }
113
+
114
+
115
+ def get_schemas_from_sql(sql_query: str, schema_mapping: dict):
116
+ import sqlglot
117
+
118
+ parsed_query = sqlglot.parse_one(sql_query, read="mysql")
119
+
120
+ # Lấy danh sách bảng duy nhất trong query
121
+ table_names = list({t.name for t in parsed_query.find_all(sqlglot.exp.Table)})
122
+
123
+ schemas_used = {}
124
+ for table in table_names:
125
+ if table in schema_mapping:
126
+ schemas_used[table] = schema_mapping[table]
127
+ else:
128
+ print(f"⚠️ Warning: Table '{table}' not found in schema_mapping")
129
+
130
+ # Gom toàn bộ schema thành 1 chuỗi duy nhất
131
+ all_schemas = "\n\n".join(
132
+ [f"Schema for table '{table}':\n{schemas_used[table]}" for table in schemas_used]
133
+ )
134
+
135
+ return all_schemas
136
+
137
+
138
+ def build_sql_fix_prompt(schemas_result: dict, sql: str) -> str:
139
+
140
+ prompt = f"""
141
+ Bạn một chuyên gia sở dữ liệu.
142
+
143
+ Dưới đây là mô tả schema chi tiết của các bảng có trong hệ thống:
144
+ Đọc rõ và ghi nhớ tùng thuộc tính của mỗi bảng mà bạn truy vấn:
145
+ {schemas_result}
146
+
147
+ ---
148
+
149
+ Dưới đây là một câu SQL đang bị lỗi do không đúng tên bảng hoặc tên cột:
150
+
151
+ ```sql
152
+ {sql.strip()}
153
+ Yêu cầu của bạn là:
154
+
155
+ Dựa trên các schema trên, hãy kiểm tra và chỉnh sửa câu SQL sao cho:
156
+ Tên bảng, tên cột(thuộc tính) phải chính xác theo trình bày bên trong schema.
157
+ Nếu tên cột trong bảng đó tả trong schema mà trình bày khác thì phải thay đổi sao cho cú pháp sql chính xác.
158
+ Logic mục đích của truy vấn được giữ nguyên.
159
+ Chỉ trả lại phần SQL đã được chỉnh sửa (không giải thích, không chú thích, không thêm nhận xét).
160
+ Trả lời dưới dạng một truy vấn SQL duy nhất.
161
+ Không dùng pt.`language_ code` = 'EN
162
+ Với các câu hỏi liên quan đến product luôn trả về kèm theo pro_id cho mình.
163
+ - "- Tránh các lỗi như :\n"
164
+ " (1054, \"Unknown column 'oi.pro_id' in 'field list'\")\n . Luôn đảm bảo bạn không bao giờ bị lỗi này"
165
+ " (1054, \"Unknown column 'oi.note' in 'field list'\") . Luôn đảm bảo bạn không bao giờ bị lỗi này\n"
166
+ " (1054, \"Unknown column 'oi.size' in 'field list'\") . Luôn đảm bảo bạn không bao giờ bị lỗi này \n"
167
+ " (1054, \"Unknown column 'c.is_deleted' in 'on clause'\"). Luôn đảm bảo bạn không bao giờ bị lỗi này\n"
168
+ """.strip()
169
+ return prompt
170
+
171
+
172
+ def contains_delete(sql: str) -> bool:
173
+ return bool(re.search(r'\bdelete\b', sql, re.IGNORECASE))
174
+
175
+ async def execute_query_user(user_input: str, user_id: int, languages: str, role: str):
176
+ api_key = get_key.get_random_api_key()
177
+ os.environ["GOOGLE_API_KEY"] = api_key
178
+ llm1 = ChatGoogleGenerativeAI(model='gemini-2.0-flash-thinking-exp-01-21',temperature=0.2)
179
+ db = SQLDatabase.from_uri(connection_uri)
180
+ PROMPT_CUSTOM = await prompt_cus.get_prompt_custom(user_input)
181
+ check_insert = is_insert_related_to_product_category_variant(user_input)
182
+
183
+ db_config = {
184
+ "host": os.getenv("DB_HOST"),
185
+ "user": os.getenv("DB_USER"),
186
+ "database": os.getenv("DB_NAME"),
187
+ "password": os.getenv("DB_PASSWORD"),
188
+ "port": int(os.getenv("DB_PORT", 3306)),
189
+ "charset": "utf8mb4",
190
+ "cursorclass": pymysql.cursors.DictCursor,
191
+ }
192
+
193
+ def regenerate_sql_until_safe():
194
+ max_retry = 5
195
+ retry_count = 0
196
+
197
+ while retry_count < max_retry:
198
+ try:
199
+ regenerated_data = db_chain.run(f"""
200
+ Role: {text_role}
201
+ Language: {languages}
202
+ Question: {user_input}.
203
+ """)
204
+ regenerated_sql = extract_sql_from_response(regenerated_data)
205
+ if regenerated_sql:
206
+ regenerated_sql = clean_sql(regenerated_sql)
207
+ if not re.search(r"%{1,2}s", regenerated_sql): # đã sạch
208
+ return regenerated_sql
209
+ retry_count += 1
210
+ except Exception as e:
211
+ return f"❌ Lỗi khi tạo lại truy vấn lần {retry_count + 1}: {str(e)}"
212
+ return "❌ Lỗi: Không thể tạo được truy vấn an toàn sau nhiều lần thử."
213
+ def execute_query_with_pymysql(query, multi=False):
214
+ connection = pymysql.connect(**db_config)
215
+ try:
216
+ with connection.cursor() as cursor:
217
+ results = []
218
+ if multi:
219
+ statements = sqlparse.split(query)
220
+ for stmt in statements:
221
+ stmt = stmt.strip()
222
+ if stmt:
223
+ try:
224
+ cursor.execute(stmt)
225
+ try:
226
+ results.append(cursor.fetchall())
227
+ except pymysql.ProgrammingError:
228
+ results.append("✅ Executed")
229
+ except Exception as e:
230
+ results.append(f"❌ Error in query: {stmt}\n{str(e)}")
231
+ else:
232
+ try:
233
+ cursor.execute(query)
234
+ results = cursor.fetchall()
235
+ except Exception as e:
236
+ return f"❌ Error executing query: {str(e)}"
237
+ connection.commit()
238
+ return results
239
+ except pymysql.MySQLError as e:
240
+ return f"❌ MySQL Error: {str(e)}"
241
+ finally:
242
+ connection.close()
243
+
244
+ def clean_sql(sql) -> str:
245
+ if isinstance(sql, dict) and sql:
246
+ first_value = next(iter(sql.values()))
247
+ sql = first_value
248
+ sql = re.sub(r"```sql", "", sql, flags=re.IGNORECASE)
249
+ sql = sql.replace("```sql", "")
250
+ sql = re.sub(r"```", "", sql)
251
+ return sql.strip()
252
+
253
+ def extract_sql_from_response(data):
254
+ # Định dạng markdown block [SQL: ```sql ... ```]
255
+ match = re.search(r"\[SQL:\s*```sql\s*(.*?)\s*```]", data, re.DOTALL)
256
+ if match:
257
+ return clean_sql(match.group(1))
258
+
259
+ # Định dạng đơn giản SQLQuery: ...
260
+ match = re.search(r"SQLQuery:\s*(.*)", data, re.DOTALL)
261
+ if match:
262
+ return clean_sql(match.group(1))
263
+
264
+ return None
265
+
266
+ def extract_sql_from_error(error_msg):
267
+ # Case 1: [SQL: ```sql ... ```]
268
+ match = re.search(r"\[SQL:\s*```sql\s*(.*?)\s*```]", error_msg, re.DOTALL)
269
+ if match:
270
+ return clean_sql(match.group(1))
271
+ match = re.search(r"```(?:sql)?\s*\r?\n(.*?)```", error_msg, re.DOTALL)
272
+ if match:
273
+ return clean_sql(match.group(1))
274
+
275
+ return None
276
+
277
+ def process_and_execute_sql(sql):
278
+ data = sql
279
+ if isinstance(data, dict) and data:
280
+ first_key, first_value = next(iter(data.items()))
281
+ sql = first_value
282
+ elif isinstance(data, list) and data:
283
+ sql = "\n\n".join(data)
284
+ sql_clean = clean_sql(sql)
285
+ if re.search(r"%{1,2}s", sql_clean):
286
+ regenerated_sql = regenerate_sql_until_safe()
287
+ sql_clean = clean_sql(regenerated_sql)
288
+ result = get_schemas_from_sql(sql_clean, schema_mapping)
289
+ print("result",result)
290
+ prompt = build_sql_fix_prompt(result,sql =sql_clean)
291
+ from advance_shopping.call_gemini import tool_call
292
+ data = tool_call.generate(prompt = prompt)
293
+ sql_clean = clean_sql(data)
294
+ print("SQL step2: ", sql_clean)
295
+ if sql_clean == None:
296
+ return " Lỗi không thể thực thi truy vấn: Không tìm thấy SQL trong phản hồi."
297
+ if contains_delete(sql_clean):
298
+ return "Lỗi: Bạn không dược phép thực hiện truy vấn DELETE trong hệ thống này."
299
+ if re.search(r"\\bIF\\b.*\\bTHEN\\b", sql_clean, re.IGNORECASE):
300
+ return "❌ Lỗi: Không được dùng IF...THEN trong SQL. Vui lòng chia nhỏ truy vấn."
301
+
302
+ if check_insert:
303
+ check_syntax = filter_syntax_sql(sql_clean, PROMPT_CUSTOM, user_input)
304
+
305
+ if check_syntax is True:
306
+ try:
307
+ connection = pymysql.connect(**db_config)
308
+ with connection.cursor() as cursor:
309
+ statements = sqlparse.split(sql_clean)
310
+ results = []
311
+ for stmt in statements:
312
+ stmt = stmt.strip()
313
+ if stmt:
314
+ try:
315
+ cursor.execute(stmt)
316
+ try:
317
+ results.append(cursor.fetchall())
318
+ except:
319
+ results.append("✅ OK")
320
+ except Exception as e:
321
+ return f"❌ Lỗi tại truy vấn: `{stmt}`\nChi tiết: {str(e)}"
322
+ connection.commit()
323
+ return results
324
+ except Exception as e:
325
+ return f"❌ Lỗi khi thực thi từng truy vấn: {str(e)}"
326
+ finally:
327
+ connection.close()
328
+ else:
329
+ try:
330
+ regenerated_data = db_chain.run(f"""
331
+ Role: {text_role}
332
+ Language: {languages}
333
+ Question: {user_input}.
334
+ """)
335
+ regenerated_sql = extract_sql_from_response(regenerated_data)
336
+ if regenerated_sql:
337
+ return process_and_execute_sql(regenerated_sql)
338
+ else:
339
+ return "❌ Lỗi: Không thể tạo lại truy vấn hợp lệ."
340
+ except Exception as regen_error:
341
+ return f"❌ Lỗi khi tạo lại truy vấn: {str(regen_error)}"
342
+ else:
343
+ return execute_query_with_pymysql(sql_clean, multi=True)
344
+
345
+ if "Do not use IF...THEN" not in PROMPT_CUSTOM.template:
346
+ PROMPT_CUSTOM.template += (
347
+ "\n\n⚠️ Note: Do NOT use IF...THEN...ELSE...END in SQL. "
348
+ "Only use plain SELECT, INSERT, UPDATE, DELETE, SET statements."
349
+ )
350
+
351
+ db_chain = SQLDatabaseChain.from_llm(llm=llm1, db=db, prompt=PROMPT_CUSTOM)
352
+ text_role = f"{role} (userId = {user_id})" if role == "ADMIN" else f"{role} (userId = {user_id}), not role ADMIN"
353
+ try:
354
+ data = db_chain.run(f"""
355
+ Role: {text_role}
356
+ Language: {languages}
357
+ Question: {user_input}.
358
+ """)
359
+ extracted_sql = extract_sql_from_response(data)
360
+ if extracted_sql:
361
+ print("SQL:",extracted_sql)
362
+ print(extracted_sql)
363
+ return process_and_execute_sql(extracted_sql)
364
+ else:
365
+ return data
366
+
367
+ except Exception as e:
368
+ error_message = str(e)
369
+ print("Lỗi: ",error_message)
370
+ extracted_sql = extract_sql_from_error(error_message)
371
+ print("SQL lỗi: ",extracted_sql)
372
+ if extracted_sql == None:
373
+ return "❌ Lỗi không thể thực thi truy vấn: Không tìm thấy SQL trong phản hồi."
374
+ fix_sql = re.sub(r"```sql", "", extracted_sql, flags=re.IGNORECASE)
375
+ fix_sql = re.sub(r"```", "", fix_sql)
376
+ fix_sql = re.sub(r'%%s', r'%s', fix_sql)
377
+ print(fix_sql)
378
+ print("SQL fix:",fix_sql)
379
+ if contains_delete(fix_sql):
380
+ return "Lỗi: Bạn không dược phép thực hiện truy vấn DELETE trong hệ thống này."
381
+ if extracted_sql:
382
+ return process_and_execute_sql(fix_sql)
383
+ else:
384
+ return f"❌ Lỗi không thể thực thi truy vấn: {error_message}"
385
+
386
+
387
+ async def create_new_chat_history(user_id: int) -> str:
388
+ """
389
+ Tạo một đoạn chat mới cho user_id và trả về ObjectId của đoạn chat.
390
+ """
391
+ check = UserRepository.getUserByUserId(user_id)
392
+ if check is None:
393
+ raise HTTPException(status_code=404, detail="User not found or has been deleted in MySQL")
394
+
395
+ user = User.objects(user_id=user_id).first()
396
+ if not user:
397
+ user = User(id=ObjectId(), user_id=user_id, user_name=f"User_{user_id}")
398
+ user.save()
399
+ random_name_chat = str(random.randint(10**14, 10**15 - 1))
400
+ name_chat = f"Chat_{random_name_chat}"
401
+ new_chat = ChatHistory(id=ObjectId(), user=user, name_chat=name_chat)
402
+ new_chat.save()
403
+
404
+ return res_chat.CreateNewChat(idMongo=str(new_chat.id), chat_name=name_chat)
405
+
406
+
407
+ async def update_chat_name(chat_id: str, new_name: str,user_id:int) -> str:
408
+ """
409
+ Cập nhật name_chat của một ChatHistory dựa trên chat_id.
410
+ """
411
+ check = UserRepository.getUserByUserId(user_id)
412
+ if check is None:
413
+ raise HTTPException(status_code=404, detail="User not found or has been deleted in MySQL")
414
+
415
+ user = User.objects(user_id=user_id, is_deleted=False).first()
416
+ if not user:
417
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
418
+
419
+ chat = ChatHistory.objects(_id=ObjectId(chat_id)).first()
420
+
421
+ if not chat:
422
+ raise HTTPException(status_code=404, detail="Chat not found in MongoDB")
423
+
424
+ chat.name_chat = new_name
425
+ chat.save()
426
+
427
+ return f"Updated chat name to {new_name}"
428
+
429
+
430
+ async def soft_delete_chat(chat_id: str,user_id:int):
431
+ """
432
+ Cập nhật `is_deleted=True` và `date_deleted` cho `ChatHistory` và các `DetailChat` liên quan.
433
+ """
434
+ check = UserRepository.getUserByUserId(user_id)
435
+ if check is None:
436
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
437
+
438
+ check_history_id = UserRepository.getChatHistory(user_id,chat_id)
439
+ if check_history_id is None:
440
+ raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MySQL")
441
+
442
+ user = User.objects(user_id=user_id, is_deleted=False).first()
443
+ if not user:
444
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
445
+
446
+ chat = ChatHistory.objects(_id=ObjectId(chat_id)).first()
447
+
448
+ if not chat:
449
+ raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MongoDB")
450
+ chat.is_deleted = True
451
+ chat.date_deleted = datetime.now(pytz.UTC)
452
+ chat.save()
453
+
454
+ DetailChat.objects(chat_history=chat).update(
455
+ set__is_deleted=True,
456
+ set__date_deleted=datetime.now(pytz.UTC)
457
+ )
458
+
459
+ return {"message": "Chat and related details marked as deleted"}
460
+
461
+
462
+ from datetime import datetime, timedelta
463
+ async def chat_with_user(
464
+ user_input: str,
465
+ user_id: int,
466
+ languages: str,
467
+ role: str,
468
+ token: str,
469
+ chat_history_id: str = None,
470
+
471
+ ) -> str:
472
+ """
473
+ Xử tin nhắn của người dùng, lưu vào lịch sử chat và trả về phản hồi từ AI.
474
+ """
475
+
476
+ if role not in ["ADMIN", "CUSTOMER", "SHIPPER"]:
477
+ raise HTTPException(status_code=400, detail="ROLE not valid")
478
+ user_id = int(user_id)
479
+ if languages not in ["VN", "EN"]:
480
+ raise HTTPException(status_code=400, detail="Language not valid")
481
+
482
+ if not user_input:
483
+ raise HTTPException(status_code=400, detail="User input empty")
484
+ if not isinstance(user_id, int) or user_id <= 0:
485
+ raise HTTPException(status_code=400, detail="Invalid user_id: must be a positive integer")
486
+
487
+ languages = "Vietnamese" if languages == "VN" else "English"
488
+
489
+ check = UserRepository.getUserByUserId(user_id)
490
+ if check is None:
491
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
492
+
493
+ check_history_id = UserRepository.getChatHistory(user_id,chat_history_id)
494
+ if check_history_id is None:
495
+ raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MySQL")
496
+
497
+ user = User.objects(user_id=user_id).first()
498
+ if not user:
499
+ return {"error": "User not found or has been deleted in MongoDB"}
500
+
501
+ chat_history = None
502
+ if chat_history_id:
503
+ try:
504
+ chat_history_obj_id = ObjectId(chat_history_id)
505
+ chat_history = ChatHistory.objects(_id=chat_history_obj_id, user=user).first()
506
+ except Exception as e:
507
+
508
+ print(f"⚠️ Invalid chat_history_id: {e}")
509
+ if not chat_history:
510
+ raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MongoDB")
511
+ # filtered_input = filter_sql_injection_1.filter_sql_injection(user_input)
512
+ # filtered_role_input = filter_role_1.filter_role(filtered_input)
513
+ # result = await execute_query_user(filtered_role_input, user_id, languages, role)
514
+ # result_final = query_result_1.query_result(user_input, result)
515
+ result_final = await pipeline_agent.multi_query_user(user_input,user_id,role,languages,chat_history_id, token)
516
+
517
+ detail_chat = DetailChat(
518
+ id=ObjectId(),
519
+ chat_history=chat_history,
520
+ you_message=user_input,
521
+ ai_message=result_final,
522
+ timestamp=datetime.now(pytz.UTC)
523
+ )
524
+ detail_chat.save()
525
+ chat_history_messages = DetailChat.objects(chat_history=chat_history).order_by('timestamp')
526
+
527
+
528
+ def convert_to_vn_time(timestamp):
529
+ return (timestamp + timedelta(hours=7)).strftime('%Y-%m-%dT%H:%M:%S')
530
+
531
+ sorted_messages = sorted(chat_history_messages, key=lambda msg: msg.timestamp, reverse=True)
532
+
533
+ formatted_messages = [
534
+ {
535
+ "index": i + 1, # Đánh số từ 1
536
+ "id": str(msg.id),
537
+ "you_message": msg.you_message,
538
+ "ai_message": msg.ai_message,
539
+ "timestamp": convert_to_vn_time(msg.timestamp)
540
+ }
541
+ for i, msg in enumerate(sorted_messages)
542
+ ]
543
+
544
+ return jsonable_encoder({
545
+ "new_message": {
546
+ "id": str(detail_chat.id),
547
+ "you_message": detail_chat.you_message,
548
+ "ai_message": detail_chat.ai_message,
549
+ "timestamp": convert_to_vn_time(detail_chat.timestamp)
550
+ },
551
+ "previous_messages": formatted_messages
552
+ })
553
+
554
+ from bson import ObjectId
555
+
556
+
557
+ async def get_chat_details(chat_id: str,user_id:int):
558
+ """
559
+ Lấy tất cả `DetailChat` thuộc `ChatHistory` có `chat_id`, chỉ lấy bản ghi `is_deleted=False`.
560
+ """
561
+
562
+ check = UserRepository.getUserByUserId(user_id)
563
+ if check is None:
564
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
565
+
566
+ check_history_id = UserRepository.getChatHistory(user_id,chat_id)
567
+ if check_history_id is None:
568
+ raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MySQL")
569
+
570
+ user = User.objects(user_id=user_id, is_deleted=False).first()
571
+ if not user:
572
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
573
+
574
+ chat = ChatHistory.objects(_id=ObjectId(chat_id), is_deleted=False).first()
575
+
576
+ if not chat:
577
+ raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MongoDB")
578
+
579
+ chat_details = DetailChat.objects(chat_history=chat, is_deleted=False)
580
+ def convert_to_vn_time(timestamp):
581
+ return (timestamp + timedelta(hours=7)).strftime('%Y-%m-%dT%H:%M:%S')
582
+
583
+ list_detail_response = [
584
+ res_chat.DetailResponse(
585
+ id=str(index + 1), # ✅ Đánh số lại từ 1
586
+ you_message=detail.you_message,
587
+ ai_message=detail.ai_message,
588
+ timestamp=convert_to_vn_time(detail.timestamp) # ✅ Chuyển sang GMT+7
589
+ )
590
+ for index, detail in enumerate(sorted(chat_details, key=lambda d: d.timestamp, reverse=True)) # ✅ Sắp xếp giảm dần
591
+ ]
592
+
593
+ return res_chat.ListDetailResponse(
594
+ chat_id=str(chat.id),
595
+ chat_name=chat.name_chat,
596
+ list_detail_response=list_detail_response
597
+ )
598
+
599
+
600
+
601
+ async def regenerate(
602
+ user_question_new: str,
603
+ user_id: int,
604
+ languages: str,
605
+ role: str,
606
+ chat_history_id: str = None
607
+ ) -> str:
608
+ """
609
+ Xử tin nhắn của người dùng, lưu vào lịch sử chat và trả về phản hồi từ AI.
610
+ """
611
+ PROMPT_CUSTOM = await prompt_cus.get_prompt_custom(user_question_new)
612
+ if role not in ["ADMIN", "CUSTOMER", "SHIPPER"]:
613
+ raise HTTPException(status_code=400, detail="ROLE not valid")
614
+ user_id = int(user_id)
615
+ if languages not in ["VN", "EN"]:
616
+ raise HTTPException(status_code=400, detail="Language not valid")
617
+
618
+ if not user_question_new:
619
+ raise HTTPException(status_code=400, detail="User input empty")
620
+ if not isinstance(user_id, int) or user_id <= 0:
621
+ raise HTTPException(status_code=400, detail="Invalid user_id: must be a positive integer")
622
+
623
+ languages = "Vietnamese" if languages == "VN" else "English"
624
+
625
+ check = UserRepository.getUserByUserId(user_id)
626
+ if check is None:
627
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
628
+
629
+ check_history_id = UserRepository.getChatHistory(user_id,chat_history_id)
630
+ if check_history_id is None:
631
+ raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MySQL")
632
+
633
+ user = User.objects(user_id=user_id).first()
634
+ if not user:
635
+ return {"error": "User not found or has been deleted in MongoDB"}
636
+
637
+ chat_history = None
638
+ if chat_history_id:
639
+ try:
640
+ chat_history_obj_id = ObjectId(chat_history_id) # Chuyển đổi sang ObjectId
641
+ chat_history = ChatHistory.objects(_id=chat_history_obj_id, user=user).first()
642
+ except Exception as e:
643
+
644
+ print(f"⚠️ Invalid chat_history_id: {e}")
645
+ if not chat_history:
646
+ raise HTTPException(status_code=400, detail="Chat history not found or has been deleted in MongoDB")
647
+ # filtered_input = filter_sql_injection_1.filter_sql_injection(user_input)
648
+ # filtered_role_input = filter_role_1.filter_role(filtered_input)
649
+ # result = await execute_query_user(filtered_role_input, user_id, languages, role)
650
+ # result_final = query_result_1.query_result(user_input, result)
651
+ if chat_history:
652
+ last_chat = (
653
+ DetailChat.objects(chat_history=chat_history, is_deleted=False)
654
+ .order_by("-timestamp")
655
+ .first()
656
+ )
657
+
658
+ # if last_chat:
659
+ # print(f"Last chat - You: {last_chat.you_message}, AI: {last_chat.ai_message}")
660
+ # else:
661
+ # print("⚠️ No chat details found for this history.")
662
+ result_final = await pipeline_agent.multi_query_user(user_question_new,user_id,role,languages,chat_history_id)
663
+ last_chat.update(set__you_message=user_question_new, set__ai_message=result_final, set__timestamp = datetime.now(pytz.UTC))
664
+
665
+ last_chat_result = (
666
+ DetailChat.objects(chat_history=chat_history, is_deleted=False)
667
+ .order_by("-timestamp")
668
+ .first()
669
+ )
670
+
671
+ # detail_chat = DetailChat(
672
+ # id=ObjectId(),
673
+ # chat_history=chat_history,
674
+ # you_message=user_input,
675
+ # ai_message=result_final,
676
+ # timestamp=datetime.now(pytz.UTC)
677
+ # )
678
+ # detail_chat.save()
679
+ chat_history_messages = DetailChat.objects(chat_history=chat_history).order_by('timestamp')
680
+
681
+
682
+ def convert_to_vn_time(timestamp):
683
+ return (timestamp + timedelta(hours=7)).strftime('%Y-%m-%dT%H:%M:%S')
684
+
685
+ sorted_messages = sorted(chat_history_messages, key=lambda msg: msg.timestamp, reverse=True)
686
+
687
+ formatted_messages = [
688
+ {
689
+ "index": i + 1, # Đánh số từ 1
690
+ "id": str(msg.id),
691
+ "you_message": msg.you_message,
692
+ "ai_message": msg.ai_message,
693
+ "timestamp": convert_to_vn_time(msg.timestamp)
694
+ }
695
+ for i, msg in enumerate(sorted_messages)
696
+ ]
697
+
698
+ return jsonable_encoder({
699
+ "new_message": {
700
+ "id": str(last_chat_result.id),
701
+ "you_message": last_chat_result.you_message,
702
+ "ai_message": last_chat_result.ai_message,
703
+ "timestamp": convert_to_vn_time(last_chat_result.timestamp)
704
+ },
705
+ "previous_messages": formatted_messages
706
+ })
707
+
708
+ from bson import ObjectId
709
+
710
+
711
+ async def get_user_chat_history(user_id: int):
712
+ """
713
+ API lấy danh sách tất cả các đoạn chat của một user_id.
714
+ """
715
+ check = UserRepository.getUserByUserId(user_id)
716
+ if check is None:
717
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MySQL")
718
+ user = User.objects(user_id=user_id, is_deleted=False).first()
719
+ if not user:
720
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
721
+ chat_histories = ChatHistory.objects(user=user, is_deleted=False)
722
+ chat_list = [
723
+ res_chat.ChatResponse(
724
+ chat_id=str(chat.id),
725
+ chat_name=chat.name_chat,
726
+ timestamp=chat.date_deleted if chat.is_deleted else chat.id.generation_time
727
+ )
728
+ for chat in chat_histories
729
+ ]
730
+
731
+ return res_chat.UserChatHistoryResponse(
732
+ user_id=user.user_id,
733
+ user_name=user.user_name,
734
+ chat_list=chat_list
735
+ )
736
+
737
+
738
+ from bson import ObjectId
739
+
740
+ async def get_chat_details_text(chat_id: str, user_id: int):
741
+ """
742
+ Trích xuất tất cả các chi tiết chat của một chat_id, gom thành một đoạn văn bản.
743
+ """
744
+ # Kiểm tra xem user tồn tại không
745
+ user = User.objects(user_id=user_id, is_deleted=False).first()
746
+ if not user:
747
+ raise HTTPException(status_code=400, detail="User not found or has been deleted in MongoDB")
748
+
749
+ # Kiểm tra xem chat history tồn tại không
750
+ chat = ChatHistory.objects(_id=ObjectId(chat_id), user=user, is_deleted=False).first()
751
+ if not chat:
752
+ raise HTTPException(status_code=400, detail="Chat not found or has been deleted in MongoDB")
753
+
754
+ # Lấy tất cả các chi tiết chat liên quan
755
+ chat_details = DetailChat.objects(chat_history=chat, is_deleted=False).order_by('timestamp')
756
+
757
+ if not chat_details:
758
+ return list()
759
+
760
+ # Gom tất cả các câu hỏi và câu trả lời vào danh sách
761
+ chat_text_list = []
762
+ for index, detail in enumerate(chat_details,start=1):
763
+ chat_text_list.append({
764
+ "order":str(index),
765
+ "timestamp": detail.timestamp.strftime("%Y-%m-%d %H:%M:%S"),
766
+ "you_message": detail.you_message,
767
+ "ai_message": detail.ai_message
768
+ })
769
+
770
+ return chat_text_list
771
+
772
+
773
+ import asyncio
774
+ import os
775
+ import subprocess
776
+ from datetime import datetime
777
+ from pathlib import Path
778
+ from function.analyze import main
779
+ import asyncio
780
+ from models.Database_Entity import StopSignal
781
+ from function.analyze.gemini import result_analyze
782
+ async def check_should_stop(chat_id: str, stop_event: object = None):
783
+ # Trường hợp dừng qua RAM (in-memory)
784
+ await asyncio.sleep(0.1)
785
+ if stop_event and stop_event.is_set():
786
+ print("🛑 Dừng qua stop_event.")
787
+ return {"status": "cancelled"}
788
+
789
+ # Trường hợp dừng qua MongoDB
790
+ await asyncio.sleep(0.1)
791
+ if StopSignal.objects(chat_history=chat_id, is_stopped=True).first():
792
+ print("🛑 Dừng vì có StopSignal trong DB.")
793
+ return {"status": "cancelled"}
794
+
795
+ return None
796
+
797
+ from typing import Optional
798
+ async def generate_and_save_code(question: str, user_id: int, role, languages: str,stop_event: Optional[asyncio.Event], filename: str = "analyze_result.py",chat_id: str = ""):
799
+ result_check = await check_should_stop(chat_id, stop_event)
800
+ if result_check:
801
+ return result_check
802
+ code_test, folder_name = await main.analyze(
803
+ question,
804
+ user_id,
805
+ languages,
806
+ role,
807
+ chat_id,
808
+ stop_event
809
+ )
810
+ result_check = await check_should_stop(chat_id, stop_event)
811
+ if result_check:
812
+ return result_check
813
+ code_clean = code_test.strip()
814
+ if code_clean.startswith("```python"):
815
+ code_clean = code_clean[9:].strip()
816
+ if code_clean.endswith("```"):
817
+ code_clean = code_clean[:-3].strip()
818
+ result_check = await check_should_stop(chat_id, stop_event)
819
+ if result_check:
820
+ return result_check
821
+ # code_clean = code_clean.replace("else:", "").strip()
822
+ code_clean = code_clean.replace("os_path:", "os.path").strip()
823
+ encoding_fix = 'import sys\nsys.stdout.reconfigure(encoding="utf-8")\n\n'
824
+ encoding_fix1= 'import numpy as np\n\n'
825
+ code_clean = encoding_fix + encoding_fix1 + code_clean
826
+
827
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
828
+ output_dir = Path(f"./Temp/{folder_name}_{timestamp}")
829
+ output_dir.mkdir(parents=True, exist_ok=True)
830
+
831
+ file_path = output_dir / filename
832
+ with open(file_path, "w", encoding="utf-8") as f:
833
+ f.write(code_clean)
834
+
835
+
836
+ env = os.environ.copy()
837
+ result_check = await check_should_stop(chat_id, stop_event)
838
+ if result_check:
839
+ return result_check
840
+ env["OUTPUT_DIR"] = str(output_dir)
841
+ result = subprocess.run(
842
+ ["python", filename],
843
+ capture_output=True,
844
+ text=True,
845
+ env=env,
846
+ cwd=output_dir,
847
+ encoding="utf-8",
848
+ errors="replace"
849
+ )
850
+ result_check = await check_should_stop(chat_id, stop_event)
851
+ if result_check:
852
+ return result_check
853
+ output_folder = str(output_dir)
854
+ absolute_path = os.path.abspath(output_folder)
855
+ final_path = os.path.join(absolute_path, "test5")
856
+ result_check = await check_should_stop(chat_id, stop_event)
857
+ if result_check:
858
+ return result_check
859
+ result_final = result_analyze.generate(image_folder=final_path,question=question)
860
+ result_check = await check_should_stop(chat_id, stop_event)
861
+ if result_check:
862
+ return result_check
863
+ return result_final, final_path