Spaces:
Sleeping
Sleeping
| import asyncio | |
| import logging | |
| import os | |
| import sys | |
| import uuid | |
| from pathlib import Path | |
| import gradio as gr | |
| from dotenv import load_dotenv | |
| # ํ๊ฒฝ ๋ณ์ ๋ก๋ (์์ด์ ํธ/ํธ๋ ์ด์ฑ import ์ด์ ์ ์คํ) | |
| load_dotenv() | |
| # ํ๋ก์ ํธ ๋ฃจํธ๋ฅผ ๊ฒฝ๋ก์ ์ถ๊ฐ | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| from src.agent.graph import agent | |
| from src.agent.state import AgentState | |
| # ๋ก๊น ์ค์ (WARNING ์ด์๋ง ์ถ๋ ฅ) | |
| logging.basicConfig( | |
| level=logging.WARNING, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| # ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก๊ทธ๋ WARNING๋ง | |
| logging.getLogger("httpx").setLevel(logging.WARNING) | |
| logging.getLogger("httpcore").setLevel(logging.WARNING) | |
| logging.getLogger("langsmith").setLevel(logging.WARNING) | |
| # CodeWeaver ๋ชจ๋ ๋ก๊ทธ๋ WARNING๋ง (๋ก๊ทธ ๋นํ์ฑํ) | |
| logging.getLogger("src.agent").setLevel(logging.WARNING) | |
| logging.getLogger("src.tools").setLevel(logging.WARNING) | |
| logging.getLogger("src.vector_db").setLevel(logging.WARNING) | |
| logger = logging.getLogger(__name__) | |
| async def chat( | |
| message: str, | |
| history: list, | |
| thread_id: str, | |
| ) -> str: | |
| """ | |
| ์ฌ์ฉ์ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๊ณ ์์ด์ ํธ ์๋ต์ ๋ฐํํฉ๋๋ค. | |
| Args: | |
| message: ์ฌ์ฉ์ ์ ๋ ฅ ๋ฉ์์ง | |
| history: ๋ํ ๋ด์ญ (Gradio ์๋ ๊ด๋ฆฌ) | |
| thread_id: ์ธ์ ๋ณ ๊ณ ์ ID (MemorySaver๊ฐ ๋ํ ๋งฅ๋ฝ ์ถ์ ์ ์ฌ์ฉ) | |
| Returns: | |
| ์์ด์ ํธ์ ์ต์ข ๋ต๋ณ | |
| """ | |
| if not message or not message.strip(): | |
| return "์ง๋ฌธ์ ์ ๋ ฅํด์ฃผ์ธ์." | |
| try: | |
| # ์ด๊ธฐ ์ํ ์์ฑ (Pydantic BaseModel ์ฌ์ฉ) | |
| from langchain_core.messages import HumanMessage | |
| initial_state = AgentState( | |
| user_question=message.strip(), | |
| messages=[HumanMessage(content=message.strip())], | |
| conversation_history=history[-5:] if history else None, # ์ต๊ทผ 5ํด๋ง ์ ๋ฌ | |
| ) | |
| # ์ธ์ ๋ณ thread_id๋ฅผ config์ ์ ๋ฌ (MemorySaver๊ฐ ๋ํ ๋งฅ๋ฝ ์ ์ง) | |
| config = {"configurable": {"thread_id": thread_id}} | |
| # ์์ด์ ํธ ์คํ | |
| result = await agent.ainvoke(initial_state, config=config) | |
| # ์ต์ข ๋ต๋ณ ์ถ์ถ | |
| final_answer = result.get("final_answer", "๋ต๋ณ์ ์์ฑํ์ง ๋ชปํ์ต๋๋ค.") | |
| return final_answer | |
| except Exception as e: | |
| logger.error("์๋ฌ ๋ฐ์: %s", e, exc_info=True) | |
| return f"โ ๏ธ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}\n๋ค์ ์๋ํด์ฃผ์ธ์." | |
| def create_demo() -> gr.Blocks: | |
| """Gradio ์ธํฐํ์ด์ค๋ฅผ ์์ฑํฉ๋๋ค.""" | |
| # CSS ์คํ์ผ (๊น๋ํ ๋์์ธ) | |
| # - Gradio ๊ธฐ๋ณธ CSS๊ฐ .contain/.gradio-container ํญ์ ๋ฎ์ด์ฐ๋ ๊ฒฝ์ฐ๊ฐ ์์ด | |
| # ๋ ๋ค !important๋ก ๊ณ ์ ํ์ฌ "์ฒ์๋ถํฐ ๋์ ํญ"์ ํ์คํ ์ ์งํฉ๋๋ค. | |
| css = """ | |
| .gradio-container { | |
| max-width: 1280px !important; | |
| width: min(1280px, 100%) !important; | |
| margin: 0 auto !important; | |
| } | |
| .contain { | |
| max-width: 1280px !important; | |
| width: min(1280px, 100%) !important; | |
| margin: 0 auto !important; | |
| padding-top: 1.5rem; | |
| } | |
| .message { font-size: 1.1rem; line-height: 1.6; } | |
| """ | |
| with gr.Blocks( | |
| title="CodeWeaver - AI ๊ฐ๋ฐ ๋์ฐ๋ฏธ", | |
| theme=gr.themes.Soft(), | |
| css=css | |
| ) as demo: | |
| gr.Markdown(""" | |
| # ๐ค CodeWeaver | |
| ### AI ๊ธฐ๋ฐ ๊ฐ๋ฐ ์ง๋ฌธ ๋ต๋ณ ์์คํ | |
| ์ด๋ณด ๊ฐ๋ฐ์๋ฅผ ์ํ ์น์ ํ AI ๋์ฐ๋ฏธ์ ๋๋ค. | |
| **์ฃผ์ ๊ธฐ๋ฅ:** | |
| - โ ์๋ฌ ํด๊ฒฐ (๋๋ฒ๊น ) | |
| - โ ๊ฐ๋ ํ์ต | |
| - โ ์ฝ๋ ๋ฆฌ๋ทฐ ๋ฐ ๊ฐ์ ์ ์ | |
| - โ **๋ค์ค ์ง๋ฌธ ์ฒ๋ฆฌ** (์ต๋ 2๊ฐ๊น์ง ๋์ ์ฒ๋ฆฌ) | |
| - โ **๋ํ ๋งฅ๋ฝ ์ดํด** (์ด์ ๋ํ๋ฅผ ์ฐธ๊ณ ํ ํ์ ์ง๋ฌธ ๋ต๋ณ) | |
| - โ **์ค๋งํธ ์บ์ฑ** (์ ์ฌ ์ง๋ฌธ ์ฆ์ ๋ต๋ณ) | |
| - โ **์๋ ๊ฒ์ ๊ฐ์ ** (๊ฒฐ๊ณผ ๋ถ์กฑ ์ ์ฟผ๋ฆฌ ์๋ ์ต์ ํ) | |
| ๐ฌ ๊ฐ๋ฐ ๊ด๋ จ ์ง๋ฌธ์ ์์ ๋กญ๊ฒ ํด๋ณด์ธ์! | |
| - ๋จ์ผ ์ง๋ฌธ: "Spring Boot JPA N+1 ๋ฌธ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์?" | |
| - ๋ค์ค ์ง๋ฌธ: "JWT๊ฐ ๋ญ์ผ? CORS๋?" (์ต๋ 2๊ฐ) | |
| - ํ์ ์ง๋ฌธ: "์ข ๋ ์ฝ๊ฒ ์ค๋ช ํด์ค" (์ด์ ๋ต๋ณ ์ฐธ๊ณ ) | |
| """) | |
| # ์ธ์ ๋ณ ๊ณ ์ ID (๋ธ๋ผ์ฐ์ ์ธ์ ๋ง๋ค ๋ ๋ฆฝ์ ์ผ๋ก ์์ฑ) | |
| session_id = gr.State(value=lambda: str(uuid.uuid4())) | |
| # ์ฑํ ์ธํฐํ์ด์ค | |
| chatbot_interface = gr.ChatInterface( | |
| fn=chat, | |
| examples=None, # examples๋ ์๋ Accordion์์ ์๋ ์ฒ๋ฆฌ | |
| chatbot=gr.Chatbot(height=500), | |
| textbox=gr.Textbox( | |
| placeholder="์ง๋ฌธ์ ์ ๋ ฅํ์ธ์...", | |
| container=False, | |
| scale=7 | |
| ), | |
| retry_btn=None, | |
| undo_btn=None, | |
| clear_btn="๐๏ธ ๋ํ ์ด๊ธฐํ", | |
| additional_inputs=[session_id], # thread_id ์ ๋ฌ | |
| ) | |
| # Clear ๋ฒํผ ํด๋ฆญ ์ ์ ์ธ์ ID ์์ฑ (์ ๋ํ ์์) | |
| def reset_session(): | |
| new_id = str(uuid.uuid4()) | |
| return new_id | |
| # Clear ๋ฒํผ์ ์ธ์ ๋ฆฌ์ ํธ๋ค๋ฌ ์ถ๊ฐ | |
| if chatbot_interface.clear_btn: | |
| chatbot_interface.clear_btn.click( | |
| reset_session, | |
| None, | |
| session_id, | |
| queue=False | |
| ) | |
| # ๋น ๋ฅธ ์ง๋ฌธ ๋ฒํผ๋ค (Accordion ๋ฐ์ผ๋ก ๋ถ๋ฆฌ) | |
| gr.Markdown("### ๐ฌ ์์ ์ง๋ฌธ") | |
| example_questions = [ | |
| "Spring Boot JPA N+1 ๋ฌธ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์?", | |
| "ImportError: No module named 'requests' ํด๊ฒฐ ๋ฐฉ๋ฒ", | |
| "Docker Compose ์ค์ ์์ ๋ฅผ ์๋ ค์ฃผ์ธ์", | |
| "์ด ์ฝ๋๋ฅผ ๊ฐ์ ํด์ฃผ์ธ์: for i in range(len(arr)): print(arr[i])", | |
| "JWT๊ฐ ๋ญ์ผ? CORS๋?", # ๋ค์ค ์ง๋ฌธ ์์ | |
| ] | |
| with gr.Row(): | |
| for question in example_questions: | |
| btn = gr.Button( | |
| question, | |
| variant="secondary", | |
| size="sm", | |
| scale=1, | |
| ) | |
| # ๋ฒํผ ํด๋ฆญ ์ ์ ๋ ฅ์ฐฝ์ ์๋ ์ ๋ ฅ | |
| btn.click( | |
| fn=lambda q=question: q, | |
| outputs=[chatbot_interface.textbox], | |
| ) | |
| # ์ ๋ณด ์น์ | |
| with gr.Accordion("๐ ์์คํ ์ ๋ณด", open=False): | |
| gr.Markdown(""" | |
| ### ์ฌ์ฉ๋ ๊ธฐ์ | |
| - **LLM**: Gemini 2.5 Flash Lite | |
| - **์๋ฒ ๋ฉ**: BAAI/bge-m3 (๋ก์ปฌ) | |
| - **๋ฒกํฐ DB**: Qdrant Cloud | |
| - **๊ฒ์ API**: Stack Overflow, GitHub, Tavily | |
| - **ํ๋ ์์ํฌ**: LangGraph | |
| ### ์ฃผ์ ๊ธฐ๋ฅ | |
| - ๐ **๋ณ๋ ฌ ๊ฒ์**: Stack Overflow, GitHub, ๊ณต์ ๋ฌธ์ ๋์ ๊ฒ์ | |
| - ๐พ **์๋ฏธ์ ์บ์ฑ**: ์ ์ฌ ์ง๋ฌธ(์๊ณ๊ฐ 0.85 ์ด์) ์ฆ์ ๋ต๋ณ | |
| - ๐ฏ **์๋ ๊ธฐ๋ฐ ๋ผ์ฐํ **: debugging/learning/code_review ์๋ ๋ถ๋ฅ | |
| - ๐ **์๋ ์ฟผ๋ฆฌ ๊ฐ์ **: ๊ฒ์ ๊ฒฐ๊ณผ ๋ถ์กฑ ์ ์ต๋ 1ํ ์๋ ์ต์ ํ | |
| - ๐ **์ด๋ณด์ ์นํ ๋ต๋ณ**: ์๋๋ณ ๋ง์ถคํ ๋ต๋ณ ๊ตฌ์กฐ | |
| - ๐ **๋ค์ค ์ง๋ฌธ ์ฒ๋ฆฌ**: ๋ ๋ฆฝ ์ง๋ฌธ 2๊ฐ๊น์ง ๋ณ๋ ฌ ์ฒ๋ฆฌ | |
| - ๐ฌ **๋ํ ๋งฅ๋ฝ ์ดํด**: clarification ์ง๋ฌธ์ ํ์คํ ๋ฆฌ ๊ธฐ๋ฐ ๋ต๋ณ | |
| ### LangGraph๋ก ๊ตฌํํ ํต์ฌ ๊ธฐ๋ฅ | |
| 1. โ **Conditional Edges**: ์ง๋ฌธ ์ ํ/์บ์ ์ฌ๋ถ/๊ฒ์ ๊ฒฐ๊ณผ์ ๋ฐ๋ฅธ ๋์ ๋ผ์ฐํ | |
| 2. โ **Send API**: 3๊ฐ ๊ฒ์ ์์ค ๋ณ๋ ฌ ์คํ (fan-out/fan-in) | |
| 3. โ **Subgraph**: ๊ฒ์ ๊ฒฐ๊ณผ ํํฐ๋ง ๋ฐ ์์ฝ ํ์ดํ๋ผ์ธ | |
| 4. โ **Map-Reduce**: ๋ค์ค ์ง๋ฌธ ์ฒ๋ฆฌ ์ ๊ฐ ์ง๋ฌธ๋ณ ๋ ๋ฆฝ ์คํ ํ ๊ฒฐ๊ณผ ํตํฉ | |
| 5. โ **Checkpointing**: MemorySaver๋ก ๋ํ ์ํ ์ ์ฅ ๋ฐ ์ฌ๊ฐ | |
| 6. โ **Pydantic Typed State**: ํ์ ์์ ํ ์ํ ๊ด๋ฆฌ | |
| ### GitHub | |
| [ํ๋ก์ ํธ ์์ค์ฝ๋](https://github.com/shin-heewon/codeweaver) | |
| """) | |
| # ์ฌ์ฉ ๊ฐ์ด๋ | |
| with gr.Accordion("๐ก ์ฌ์ฉ ํ", open=False): | |
| gr.Markdown(""" | |
| ### 1. ๊ตฌ์ฒด์ ์ผ๋ก ์ง๋ฌธํ๊ธฐ | |
| - โ "ํ์ด์ฌ ์๋ฌ" | |
| - โ "ImportError: No module named 'requests' ํด๊ฒฐ ๋ฐฉ๋ฒ" | |
| ### 2. ์ง๋ฌธ ์ ํ๋ณ ์์ | |
| - **๋๋ฒ๊น **: "์ด ์๋ฌ ๋ฉ์์ง๋ ๋ฌด์์ ์๋ฏธํ๋์?" | |
| - **ํ์ต**: "JPA N+1 ๋ฌธ์ ๋ ์ ๋ฐ์ํ๋์?" | |
| - **์ฝ๋ ๋ฆฌ๋ทฐ**: "์ด ์ฝ๋๋ฅผ ๋ ํจ์จ์ ์ผ๋ก ๊ฐ์ ํ๋ ค๋ฉด?" | |
| ### 3. ๋ค์ค ์ง๋ฌธ ์ฌ์ฉ๋ฒ | |
| - โ **2๊ฐ๊น์ง ๊ฐ๋ฅ**: "JWT๊ฐ ๋ญ์ผ? CORS๋?" | |
| - โ **3๊ฐ ์ด์ ๋ถ๊ฐ**: "JWT? CORS? Docker?" โ ์๋ด ๋ฉ์์ง ํ์ | |
| - ๐ก **ํ**: ๊ด๋ จ ์ง๋ฌธ์ ํ๋๋ก ํตํฉํ๊ฑฐ๋, ์์ฐจ์ ์ผ๋ก ์ง๋ฌธํ์ธ์ | |
| ### 4. ๋ํ ๋งฅ๋ฝ ํ์ฉ | |
| - **ํ์ ์ง๋ฌธ**: "์ข ๋ ์ฝ๊ฒ ์ค๋ช ํด์ค", "์์ ์ฝ๋๋ก ๋ณด์ฌ์ค" | |
| - **์ ๊ฐ๋ ์ง๋ฌธ**: ๋ํ ์ค์๋ "Event Listener๋ ๋ญ์ผ?" ๊ฐ์ ๋ ๋ฆฝ ์ง๋ฌธ ๊ฐ๋ฅ | |
| - ๐ก **ํ**: ์ด์ ๋ํ๋ฅผ ์ฐธ๊ณ ํ ๋ต๋ณ์ด ํ์ํ๋ฉด ์์ฐ์ค๋ฝ๊ฒ ์ง๋ฌธํ์ธ์ | |
| ### 5. ์๋ต ์๊ฐ | |
| - **์ฒซ ์ง๋ฌธ**: 10~15์ด ์์ (๊ฒ์ + ๋ต๋ณ ์์ฑ) | |
| - **์ ์ฌ ์ง๋ฌธ**: ์ฆ์ ๋ต๋ณ (์บ์ ํ์ฉ, ์๊ณ๊ฐ 0.85 ์ด์) | |
| - **๋ค์ค ์ง๋ฌธ**: ๊ฐ ์ง๋ฌธ๋ณ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ก ํจ์จ์ | |
| ### 6. ๋ ๋์ ๋ต๋ณ์ ์ํ ํ | |
| - ์๋ฌ ๋ฉ์์ง๋ฅผ ํฌํจํด์ฃผ์ธ์ | |
| - ์ฌ์ฉ ์ค์ธ ์ธ์ด/ํ๋ ์์ํฌ๋ฅผ ๋ช ์ํ์ธ์ | |
| - ์๋ํ๋ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ํจ๊ป ์๋ ค์ฃผ์ธ์ | |
| - ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ถ์กฑํ๋ฉด ์๋์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ ํฉ๋๋ค (์ต๋ 1ํ) | |
| """) | |
| return demo | |
| # ์ฑ ์์ฑ | |
| app = create_demo() | |
| if __name__ == "__main__": | |
| # ๋ก์ปฌ ์คํ | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, # True๋ก ํ๋ฉด ๊ณต๊ฐ URL ์์ฑ | |
| show_api=False, # Gradio 4.44.x ๋ฒ๊ทธ ์ฐํ์ฉ | |
| ) | |