# ui/components.py """PROBIN UI 컴포넌트""" import streamlit as st from typing import List, Dict def render_header(): """헤더 렌더링 (PROBIN 버전)""" st.markdown( """

🧠 PROBIN

Intelligent Document Analysis System

정확도 우선 RAG 시스템

""", unsafe_allow_html=True ) def render_sources_with_relevance(sources: List[Dict], message_idx: int, move_to_page_callback): """ 출처 렌더링 (가장 관련도 높은 것 우선) Args: sources: 출처 리스트 (이미 관련도 순으로 정렬됨) message_idx: 메시지 인덱스 (키 중복 방지용) move_to_page_callback: 페이지 이동 콜백 함수 구조: 🎯 핵심 근거 (1개) - sources[0]: 가장 관련도 높은 출처 📚 추가 참고 문서 (나머지) - Expander 내부 """ if not sources: return # 첫 번째 출처 = 가장 관련도 높음 st.markdown("---") st.markdown("**🎯 핵심 근거:**") primary_source = sources[0] if st.button( f"📍 페이지 {primary_source['page_num']} 확인", key=f"primary_{message_idx}", type="primary", use_container_width=True ): move_to_page_callback(primary_source['page_num'], primary_source['text']) # 미리보기 st.caption(f"💬 \"{primary_source['text'][:100]}...\"") # 추가 참고 문서 (나머지) if len(sources) > 1: additional_sources = sources[1:] with st.expander(f"📚 추가 참고 문서 ({min(2, len(additional_sources))}개)"): cols = st.columns(2) for i, src in enumerate(additional_sources[:2]): # 최대 2개만 with cols[i]: if st.button( f"p.{src['page_num']}", key=f"additional_{message_idx}_{i}", use_container_width=True ): move_to_page_callback(src['page_num'], src['text']) st.caption(f"\"{src['text'][:60]}...\"") def render_answer(answer: str, sources: List[Dict]): """답변 및 출처 렌더링 (Legacy - 사용 안 함)""" # 답변 st.markdown("### 💡 답변") st.markdown( f"""
{answer}
""", unsafe_allow_html=True ) # 출처 if sources: st.markdown("### 📄 출처") for i, source in enumerate(sources, 1): with st.expander(f"출처 {i} - 페이지 {source['page_num']}"): st.text(source['text']) def render_file_uploader(): """파일 업로더 렌더링""" st.markdown("### 📤 PDF 업로드") uploaded_file = st.file_uploader( "RFP PDF 파일을 업로드하세요", type=["pdf"], help="PDF 형식의 RFP 문서를 업로드하세요" ) return uploaded_file def render_query_input(): """질문 입력 렌더링 (Deprecated: st.chat_input 사용 권장)""" st.markdown("### 💬 질문하기") query = st.text_input( "질문을 입력하세요", placeholder="예: 이 프로젝트의 예산은 얼마인가요?", help="RFP 문서에 대해 질문하세요" ) return query def render_chat_history(messages: list): """채팅 히스토리 렌더링 (현대적 방식)""" for message in messages: with st.chat_message(message["role"]): st.markdown(message["content"]) # 출처 표시 if message["role"] == "assistant" and "sources" in message: with st.expander("📚 출처 보기"): for i, src in enumerate(message["sources"], 1): st.markdown(f"**출처 {i} - [페이지 {src['page_num']}]**") st.caption(src["text"]) def render_sidebar(): """사이드바 렌더링""" with st.sidebar: st.markdown("## ⚙️ 설정") # 검색 설정 st.markdown("### 🔍 검색 설정") top_k = st.slider("검색할 청크 수", 5, 20, 10) # 청킹 설정 st.markdown("### ✂️ 청킹 설정") chunk_size = st.number_input("청크 크기", 400, 1200, 800, step=100) chunk_overlap = st.number_input("오버랩 크기", 50, 300, 150, step=50) st.markdown("---") # 정보 st.markdown("### ℹ️ 정보") st.info( """ **PROBIN v2.0** - PDF 업로드 ✅ - 질문-답변 ✅ - 출처 표시 ✅ - 벡터 검색 ✅ - 하이라이트 ✅ """ ) # 초기화 버튼 if st.button("🗑️ 데이터베이스 초기화", type="secondary"): return True, top_k, chunk_size, chunk_overlap return False, top_k, chunk_size, chunk_overlap def render_processing_status(status: str): """처리 상태 렌더링""" status_icons = { "uploading": "📤", "extracting": "📄", "chunking": "✂️", "embedding": "🔢", "storing": "💾", "complete": "✅" } icon = status_icons.get(status, "⏳") st.info(f"{icon} {status}") def render_welcome_message(): """웰컴 메시지 (사용 안내)""" st.markdown("""

🧠 PROBIN에 오신 것을 환영합니다!

Intelligent Document Analysis System

📖 이용 방법

  1. PDF 업로드: 왼쪽 사이드바에서 문서를 업로드하세요
  2. 문서 처리: 30초~1분 정도 기다립니다
  3. 질문 입력: 채팅창에 질문을 입력하세요
  4. 근거 확인: 노란색 하이라이트로 근거를 확인하세요
📺 Split View 🖍️ Highlighting 🎯 High Accuracy
""", unsafe_allow_html=True)