RFP_summary_chatbot / src /utils /conversation_manager.py
Dongjin1203's picture
Initial commit for HF Spaces deployment
4739096
# src/utils/conversation_manager.py
"""
๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ์ž (๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜)
๊ธฐ๋Šฅ:
- UI ํ‘œ์‹œ์šฉ / ๋ถ„์„์šฉ ํžˆ์Šคํ† ๋ฆฌ ๋ถ„๋ฆฌ
- ์ „์ฒด ๋Œ€ํ™” ์ €์žฅ (greeting, thanks, document, out_of_scope)
- JSON ๋‚ด๋ณด๋‚ด๊ธฐ
- ํ†ต๊ณ„ ๊ธฐ๋Šฅ
"""
from datetime import datetime
from typing import List, Dict, Optional
import json
import logging
logger = logging.getLogger(__name__)
class ConversationManager:
"""
๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ (๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜)
Streamlit session_state์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ:
- UI ํžˆ์Šคํ† ๋ฆฌ: Streamlit ๋ฉ”์‹œ์ง€ ํ˜•์‹
- DB ํžˆ์Šคํ† ๋ฆฌ: ๋ถ„์„/์ €์žฅ์šฉ ํ˜•์‹
"""
def __init__(self):
"""์ดˆ๊ธฐํ™”"""
self.ui_history: List[Dict] = [] # Streamlit ํ‘œ์‹œ์šฉ
self.db_history: List[Dict] = [] # ๋ถ„์„/์ €์žฅ์šฉ
logger.info("๐Ÿ’ฌ ConversationManager ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
def add_message(
self,
user_msg: str,
ai_msg: str,
query_type: str,
sources: Optional[List] = None,
usage: Optional[Dict] = None,
search_mode: Optional[str] = None,
used_retrieval: bool = False,
routing_info: Optional[Dict] = None
):
"""
๋ฉ”์‹œ์ง€ ์ถ”๊ฐ€ (์ „์ฒด ์ €์žฅ)
Args:
user_msg: ์‚ฌ์šฉ์ž ์งˆ๋ฌธ
ai_msg: AI ๋‹ต๋ณ€
query_type: ์งˆ๋ฌธ ์œ ํ˜• (greeting/thanks/document/out_of_scope)
sources: ์ฐธ๊ณ  ๋ฌธ์„œ ๋ฆฌ์ŠคํŠธ
usage: ํ† ํฐ ์‚ฌ์šฉ๋Ÿ‰
search_mode: ๊ฒ€์ƒ‰ ๋ชจ๋“œ
used_retrieval: ๊ฒ€์ƒ‰ ์‚ฌ์šฉ ์—ฌ๋ถ€
routing_info: ๋ผ์šฐํŒ… ์ •๋ณด
"""
timestamp = datetime.now()
# ===== UI ํžˆ์Šคํ† ๋ฆฌ (Streamlit ๋ฉ”์‹œ์ง€ ํ˜•์‹) =====
# ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€
self.ui_history.append({
'role': 'user',
'content': user_msg,
'timestamp': timestamp
})
# AI ๋ฉ”์‹œ์ง€
self.ui_history.append({
'role': 'assistant',
'content': ai_msg,
'sources': sources or [],
'usage': usage or {},
'search_mode': search_mode,
'used_retrieval': used_retrieval,
'routing_info': routing_info,
'type': query_type, # ๋ถ„์„์šฉ ์ถ”๊ฐ€
'timestamp': timestamp
})
# ===== DB ํžˆ์Šคํ† ๋ฆฌ (๋ถ„์„์šฉ) =====
self.db_history.append({
'user': user_msg,
'assistant': ai_msg,
'type': query_type,
'timestamp': timestamp.isoformat(),
'sources_count': len(sources) if sources else 0,
'used_retrieval': used_retrieval,
'search_mode': search_mode,
'routing_info': routing_info
})
logger.info(f"๐Ÿ’พ ๋Œ€ํ™” ์ €์žฅ: {query_type} - {user_msg[:30]}...")
def get_ui_history(self) -> List[Dict]:
"""
UI ํ‘œ์‹œ์šฉ ํžˆ์Šคํ† ๋ฆฌ ๋ฐ˜ํ™˜ (Streamlit ํ˜•์‹)
Returns:
Streamlit ๋ฉ”์‹œ์ง€ ๋ฆฌ์ŠคํŠธ
"""
return self.ui_history
def get_db_history(self, last_n: Optional[int] = None) -> List[Dict]:
"""
๋ถ„์„/์ €์žฅ์šฉ ํžˆ์Šคํ† ๋ฆฌ ๋ฐ˜ํ™˜
Args:
last_n: ์ตœ๊ทผ N๊ฐœ๋งŒ ๋ฐ˜ํ™˜ (None์ด๋ฉด ์ „์ฒด)
Returns:
๋Œ€ํ™” ๊ธฐ๋ก ๋ฆฌ์ŠคํŠธ
"""
if last_n:
return self.db_history[-last_n:]
return self.db_history
def get_history_by_type(self, query_type: str) -> List[Dict]:
"""
ํŠน์ • ์งˆ๋ฌธ ์œ ํ˜•๋งŒ ํ•„ํ„ฐ๋ง
Args:
query_type: 'greeting', 'thanks', 'document', 'out_of_scope'
Returns:
ํ•„ํ„ฐ๋ง๋œ ๋Œ€ํ™” ๋ฆฌ์ŠคํŠธ
"""
return [
msg for msg in self.db_history
if msg['type'] == query_type
]
def get_statistics(self) -> Dict[str, int]:
"""
์งˆ๋ฌธ ์œ ํ˜•๋ณ„ ํ†ต๊ณ„
Returns:
{'greeting': 5, 'document': 20, ...}
"""
from collections import Counter
types = [msg['type'] for msg in self.db_history]
stats = dict(Counter(types))
# ์ด ๋Œ€ํ™” ์ˆ˜ ์ถ”๊ฐ€
stats['total'] = len(self.db_history)
return stats
def export_to_json(self) -> str:
"""
JSON ํ˜•์‹์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ
Returns:
JSON ๋ฌธ์ž์—ด
"""
export_data = {
'timestamp': datetime.now().isoformat(),
'total_conversations': len(self.db_history),
'statistics': self.get_statistics(),
'conversations': self.db_history
}
return json.dumps(export_data, ensure_ascii=False, indent=2)
def clear(self):
"""ํžˆ์Šคํ† ๋ฆฌ ์ดˆ๊ธฐํ™”"""
self.ui_history = []
self.db_history = []
logger.info("๐Ÿ—‘๏ธ ๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ ์ดˆ๊ธฐํ™”")
def __len__(self):
"""๋Œ€ํ™” ๊ฐœ์ˆ˜ (์‚ฌ์šฉ์ž ์งˆ๋ฌธ ๊ธฐ์ค€)"""
return len(self.db_history)
def __repr__(self):
stats = self.get_statistics()
return (
f"ConversationManager("
f"total={stats.get('total', 0)}, "
f"document={stats.get('document', 0)}, "
f"greeting={stats.get('greeting', 0)}, "
f"thanks={stats.get('thanks', 0)}, "
f"out_of_scope={stats.get('out_of_scope', 0)})"
)
# ===== ํ…Œ์ŠคํŠธ ์ฝ”๋“œ =====
if __name__ == "__main__":
# ํ…Œ์ŠคํŠธ
manager = ConversationManager()
# ๋Œ€ํ™” ์ถ”๊ฐ€
manager.add_message(
user_msg="์•ˆ๋…•ํ•˜์„ธ์š”",
ai_msg="์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฌด์—‡์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”?",
query_type="greeting"
)
manager.add_message(
user_msg="์˜ˆ์‚ฐ์ด ์–ผ๋งˆ์ธ๊ฐ€์š”?",
ai_msg="์˜ˆ์‚ฐ์€ 5์–ต์›์ž…๋‹ˆ๋‹ค.",
query_type="document",
sources=[{'content': '์˜ˆ์‚ฐ: 5์–ต์›', 'score': 0.95}],
used_retrieval=True,
search_mode="hybrid_rerank"
)
manager.add_message(
user_msg="๊ณ ๋งˆ์›Œ์š”",
ai_msg="์ฒœ๋งŒ์—์š”! ์–ธ์ œ๋“  ์งˆ๋ฌธํ•˜์„ธ์š”.",
query_type="thanks"
)
# ํ†ต๊ณ„ ์ถœ๋ ฅ
print("\n===== ํ†ต๊ณ„ =====")
print(manager.get_statistics())
# ํžˆ์Šคํ† ๋ฆฌ ์ถœ๋ ฅ
print("\n===== DB ํžˆ์Šคํ† ๋ฆฌ =====")
for msg in manager.get_db_history():
print(f"{msg['type']}: {msg['user'][:20]}...")
# JSON ๋‚ด๋ณด๋‚ด๊ธฐ
print("\n===== JSON Export =====")
print(manager.export_to_json())
# Representation
print("\n===== Manager Info =====")
print(manager)