minh-4T commited on
Commit
23f0b25
·
1 Parent(s): 148671a

Update chat with cohort_key

Browse files
Files changed (3) hide show
  1. core/config.py +7 -0
  2. core/qa_pipeline.py +9 -4
  3. main.py +34 -11
core/config.py CHANGED
@@ -67,6 +67,13 @@ SUPABASE_SYNC_ALLOWED_IPS = [ip.strip() for ip in os.getenv('SUPABASE_SYNC_ALLOW
67
  SUPABASE_SYNC_ALLOW_PRIVATE_NETWORK = os.getenv('SUPABASE_SYNC_ALLOW_PRIVATE_NETWORK', 'true').strip().lower() in {'1', 'true', 'yes', 'on'}
68
  COLLECTION_ROUTER_TOP_N = _bounded_int_from_env('COLLECTION_ROUTER_TOP_N', 3, 1, 20)
69
 
 
 
 
 
 
 
 
70
  # - Context and output limits
71
  MAX_CONTEXT_CHARS = int(os.getenv('MAX_CONTEXT_CHARS', '12000'))
72
  MAX_OUT_CHARS = int(os.getenv('MAX_OUT_CHARS', '3000'))
 
67
  SUPABASE_SYNC_ALLOW_PRIVATE_NETWORK = os.getenv('SUPABASE_SYNC_ALLOW_PRIVATE_NETWORK', 'true').strip().lower() in {'1', 'true', 'yes', 'on'}
68
  COLLECTION_ROUTER_TOP_N = _bounded_int_from_env('COLLECTION_ROUTER_TOP_N', 3, 1, 20)
69
 
70
+ # Cohort to academic year mapping
71
+ COHORT_TO_YEAR = {
72
+ 'k65': '2023-2024',
73
+ 'k64': '2022-2023',
74
+ 'k63': '2021-2022',
75
+ }
76
+
77
  # - Context and output limits
78
  MAX_CONTEXT_CHARS = int(os.getenv('MAX_CONTEXT_CHARS', '12000'))
79
  MAX_OUT_CHARS = int(os.getenv('MAX_OUT_CHARS', '3000'))
core/qa_pipeline.py CHANGED
@@ -221,16 +221,16 @@ def generate_standalone_query(message: str, history: List) -> str:
221
 
222
  return message
223
 
224
- def ask_ai_improved(message: str, history: List, hybrid_retriever) -> Generator[str, None, None]:
225
  full_response = ""
226
- for delta in ask_ai_stream_delta(message, history, hybrid_retriever):
227
  full_response += delta
228
  if len(full_response) > MAX_OUT_CHARS:
229
  yield full_response[:MAX_OUT_CHARS] + "\n\n[Đã cắt bớt nội dung dài]"
230
  return
231
  yield full_response
232
 
233
- def ask_ai_stream_delta(message: str, history: List, hybrid_retriever) -> Generator[str, None, None]:
234
  if not message.strip():
235
  yield " Bạn chưa nhập câu hỏi."
236
  return
@@ -261,7 +261,12 @@ def ask_ai_stream_delta(message: str, history: List, hybrid_retriever) -> Genera
261
 
262
  all_docs: List = []
263
  seen = set()
264
- year_scope_hint = requested_year_range or (", ".join(sorted(mentioned_years)) if mentioned_years else None)
 
 
 
 
 
265
  for query in queries:
266
  #Giữ nguyên logic alpha ngành CNTT của Minh
267
  current_alpha = 0.4 if "CNTT" in query.upper() else 0.5
 
221
 
222
  return message
223
 
224
+ def ask_ai_improved(message: str, history: List, hybrid_retriever, year_scope: str | None = None) -> Generator[str, None, None]:
225
  full_response = ""
226
+ for delta in ask_ai_stream_delta(message, history, hybrid_retriever, year_scope=year_scope):
227
  full_response += delta
228
  if len(full_response) > MAX_OUT_CHARS:
229
  yield full_response[:MAX_OUT_CHARS] + "\n\n[Đã cắt bớt nội dung dài]"
230
  return
231
  yield full_response
232
 
233
+ def ask_ai_stream_delta(message: str, history: List, hybrid_retriever, year_scope: str | None = None) -> Generator[str, None, None]:
234
  if not message.strip():
235
  yield " Bạn chưa nhập câu hỏi."
236
  return
 
261
 
262
  all_docs: List = []
263
  seen = set()
264
+ # Prefer passed year_scope over detected year
265
+ if year_scope:
266
+ year_scope_hint = year_scope
267
+ logger.info(f"Sử dụng year_scope từ cohort: {year_scope_hint}")
268
+ else:
269
+ year_scope_hint = requested_year_range or (", ".join(sorted(mentioned_years)) if mentioned_years else None)
270
  for query in queries:
271
  #Giữ nguyên logic alpha ngành CNTT của Minh
272
  current_alpha = 0.4 if "CNTT" in query.upper() else 0.5
main.py CHANGED
@@ -14,6 +14,7 @@ from qdrant_client import QdrantClient
14
  #Import các model và các hàm cần thiết từ core
15
  from core.config import (
16
  COLLECTION_ROUTER_TOP_N,
 
17
  DATABASE_URL,
18
  QDRANT_API_KEY,
19
  QDRANT_URL,
@@ -62,6 +63,7 @@ async def init_db_asyncpg(pool: asyncpg.Pool):
62
  # 2 lệnh ALTER TABLE để cập nhật bảng cũ nếu đã tồn tại
63
  await conn.execute('ALTER TABLE history ADD COLUMN IF NOT EXISTS user_id TEXT')
64
  await conn.execute('ALTER TABLE history ADD COLUMN IF NOT EXISTS title TEXT')
 
65
 
66
  await conn.execute('''
67
  CREATE INDEX IF NOT EXISTS idx_history_session_id_id
@@ -99,7 +101,7 @@ async def get_history_async(pool: asyncpg.Pool, session_id: str):
99
  return []
100
 
101
  # Hàm lưu lượt chat để hỗ trợ title và user_id
102
- async def save_turn_async(pool: asyncpg.Pool, session_id: str, user_msg: str, assistant_msg: str, user_id: str = None):
103
  try:
104
  async with pool.acquire() as conn:
105
  # Kiểm tra xem session này đã có tiêu đề chưa
@@ -110,12 +112,12 @@ async def save_turn_async(pool: asyncpg.Pool, session_id: str, user_msg: str, as
110
 
111
  async with conn.transaction():
112
  await conn.execute(
113
- "INSERT INTO history (session_id, user_id, role, content, title) VALUES ($1, $2, $3, $4, $5)",
114
- session_id, user_id, "user", user_msg, title
115
  )
116
  await conn.execute(
117
- "INSERT INTO history (session_id, user_id, role, content, title) VALUES ($1, $2, $3, $4, $5)",
118
- session_id, user_id, "assistant", assistant_msg, title
119
  )
120
  except Exception:
121
  logger.exception("Lỗi khi lưu lượt hội thoại:", exc_info=True)
@@ -299,6 +301,7 @@ class ChatRequest(BaseModel):
299
  session_id: str
300
  user_id: str = None
301
  message: str
 
302
 
303
  class ChatResponse(BaseModel):
304
  response: str
@@ -339,19 +342,29 @@ async def chat_endpoint(payload: ChatRequest, request: Request):
339
 
340
  session_id = payload.session_id
341
  user_id = payload.user_id # Lấy user_id từ request
 
 
 
 
 
 
 
 
 
 
342
  history = await get_history_async(db_pool, session_id)
343
 
344
  # Tập hợp toàn bộ response từ generator
345
  full_response = ""
346
  try:
347
- async for chunk in iterate_in_threadpool(ask_ai_improved(user_msg, history, retriever)):
348
  full_response = chunk
349
  except Exception:
350
  logger.exception("Lỗi khi xử lý phản hồi từ AI:", exc_info=True)
351
  raise HTTPException(status_code=500, detail="Lỗi khi xử lý yêu cầu")
352
 
353
- # Lưu lịch sử sau khi có response đầy đủ (Kèm theo user_id)
354
- await save_turn_async(db_pool, session_id, user_msg, full_response, user_id)
355
 
356
  return ChatResponse(response=full_response)
357
 
@@ -366,6 +379,16 @@ async def chat_stream_endpoint(payload: ChatRequest, request: Request):
366
 
367
  session_id = payload.session_id
368
  user_id = payload.user_id # Lấy user_id từ request
 
 
 
 
 
 
 
 
 
 
369
  history = await get_history_async(db_pool, session_id)
370
 
371
  async def event_stream_generator():
@@ -373,7 +396,7 @@ async def chat_stream_endpoint(payload: ChatRequest, request: Request):
373
  full_response = ""
374
  try:
375
  # ask_ai_stream_delta yield từng delta chunk (không cumulative)
376
- async for delta_chunk in iterate_in_threadpool(ask_ai_stream_delta(user_msg, history, retriever)):
377
  full_response += delta_chunk
378
  # Gửi SSE event với delta chunk
379
  sse_data = json.dumps({"delta": delta_chunk, "done": False}, ensure_ascii=False)
@@ -382,8 +405,8 @@ async def chat_stream_endpoint(payload: ChatRequest, request: Request):
382
  # Gửi tín hiệu kết thúc
383
  yield 'data: {"delta": "", "done": true}\n\n'
384
 
385
- # Lưu lịch sử sau khi stream xong (Kèm theo user_id)
386
- await save_turn_async(db_pool, session_id, user_msg, full_response, user_id)
387
 
388
  except Exception:
389
  logger.exception("Lỗi khi stream phản hồi từ AI:", exc_info=True)
 
14
  #Import các model và các hàm cần thiết từ core
15
  from core.config import (
16
  COLLECTION_ROUTER_TOP_N,
17
+ COHORT_TO_YEAR,
18
  DATABASE_URL,
19
  QDRANT_API_KEY,
20
  QDRANT_URL,
 
63
  # 2 lệnh ALTER TABLE để cập nhật bảng cũ nếu đã tồn tại
64
  await conn.execute('ALTER TABLE history ADD COLUMN IF NOT EXISTS user_id TEXT')
65
  await conn.execute('ALTER TABLE history ADD COLUMN IF NOT EXISTS title TEXT')
66
+ await conn.execute('ALTER TABLE history ADD COLUMN IF NOT EXISTS cohort_key TEXT')
67
 
68
  await conn.execute('''
69
  CREATE INDEX IF NOT EXISTS idx_history_session_id_id
 
101
  return []
102
 
103
  # Hàm lưu lượt chat để hỗ trợ title và user_id
104
+ async def save_turn_async(pool: asyncpg.Pool, session_id: str, user_msg: str, assistant_msg: str, user_id: str = None, cohort_key: str = None):
105
  try:
106
  async with pool.acquire() as conn:
107
  # Kiểm tra xem session này đã có tiêu đề chưa
 
112
 
113
  async with conn.transaction():
114
  await conn.execute(
115
+ "INSERT INTO history (session_id, user_id, role, content, title, cohort_key) VALUES ($1, $2, $3, $4, $5, $6)",
116
+ session_id, user_id, "user", user_msg, title, cohort_key
117
  )
118
  await conn.execute(
119
+ "INSERT INTO history (session_id, user_id, role, content, title, cohort_key) VALUES ($1, $2, $3, $4, $5, $6)",
120
+ session_id, user_id, "assistant", assistant_msg, title, cohort_key
121
  )
122
  except Exception:
123
  logger.exception("Lỗi khi lưu lượt hội thoại:", exc_info=True)
 
301
  session_id: str
302
  user_id: str = None
303
  message: str
304
+ cohort_key: str = None
305
 
306
  class ChatResponse(BaseModel):
307
  response: str
 
342
 
343
  session_id = payload.session_id
344
  user_id = payload.user_id # Lấy user_id từ request
345
+ cohort_key = payload.cohort_key # Lấy cohort_key từ request
346
+
347
+ # Convert cohort_key to year_scope for collection routing
348
+ year_scope = None
349
+ if cohort_key and cohort_key in COHORT_TO_YEAR:
350
+ year_scope = COHORT_TO_YEAR[cohort_key]
351
+ logger.info(f"Sử dụng cohort: {cohort_key} -> năm học: {year_scope}")
352
+ elif cohort_key:
353
+ logger.warning(f"Cohort không hợp lệ: {cohort_key}")
354
+
355
  history = await get_history_async(db_pool, session_id)
356
 
357
  # Tập hợp toàn bộ response từ generator
358
  full_response = ""
359
  try:
360
+ async for chunk in iterate_in_threadpool(ask_ai_improved(user_msg, history, retriever, year_scope=year_scope)):
361
  full_response = chunk
362
  except Exception:
363
  logger.exception("Lỗi khi xử lý phản hồi từ AI:", exc_info=True)
364
  raise HTTPException(status_code=500, detail="Lỗi khi xử lý yêu cầu")
365
 
366
+ # Lưu lịch sử sau khi có response đầy đủ (Kèm theo user_id và cohort_key)
367
+ await save_turn_async(db_pool, session_id, user_msg, full_response, user_id, cohort_key)
368
 
369
  return ChatResponse(response=full_response)
370
 
 
379
 
380
  session_id = payload.session_id
381
  user_id = payload.user_id # Lấy user_id từ request
382
+ cohort_key = payload.cohort_key # Lấy cohort_key từ request
383
+
384
+ # Convert cohort_key to year_scope for collection routing
385
+ year_scope = None
386
+ if cohort_key and cohort_key in COHORT_TO_YEAR:
387
+ year_scope = COHORT_TO_YEAR[cohort_key]
388
+ logger.info(f"Sử dụng cohort: {cohort_key} -> năm học: {year_scope}")
389
+ elif cohort_key:
390
+ logger.warning(f"Cohort không hợp lệ: {cohort_key}")
391
+
392
  history = await get_history_async(db_pool, session_id)
393
 
394
  async def event_stream_generator():
 
396
  full_response = ""
397
  try:
398
  # ask_ai_stream_delta yield từng delta chunk (không cumulative)
399
+ async for delta_chunk in iterate_in_threadpool(ask_ai_stream_delta(user_msg, history, retriever, year_scope=year_scope)):
400
  full_response += delta_chunk
401
  # Gửi SSE event với delta chunk
402
  sse_data = json.dumps({"delta": delta_chunk, "done": False}, ensure_ascii=False)
 
405
  # Gửi tín hiệu kết thúc
406
  yield 'data: {"delta": "", "done": true}\n\n'
407
 
408
+ # Lưu lịch sử sau khi stream xong (Kèm theo user_id và cohort_key)
409
+ await save_turn_async(db_pool, session_id, user_msg, full_response, user_id, cohort_key)
410
 
411
  except Exception:
412
  logger.exception("Lỗi khi stream phản hồi từ AI:", exc_info=True)