Spaces:
Sleeping
Sleeping
| from typing import Any, Dict, List, Optional, Literal, Tuple, Annotated | |
| from operator import add | |
| from pydantic import BaseModel, Field | |
| from langchain_core.messages import BaseMessage | |
| from langgraph.graph import add_messages | |
| _STEPS_RESET_TOKEN = "__RESET_STEPS__" | |
| _MULTI_ANS_RESET_TOKEN = "__RESET_MULTI_ANS__" | |
| def merge_intermediate_steps(old: List[str], new: List[str]) -> List[str]: | |
| """ | |
| intermediate_steps reducer. | |
| - ๊ธฐ๋ณธ ๋์: old + new (๋ณ๋ ฌ ๋ ธ๋์์ ๋์์ step์ ์ถ๊ฐ ๊ฐ๋ฅ) | |
| - ๋ฆฌ์ ๋์: new์ ์ฒซ ์์๊ฐ _STEPS_RESET_TOKEN ์ด๋ฉด old๋ฅผ ๋ฒ๋ฆฌ๊ณ new[1:]๋ก ๊ต์ฒด | |
| (์ฒดํฌํฌ์ธํ ์ผ๋ก ๋์ ๋ step์ '์ด๋ฒ ์คํ(run)' ๊ธฐ์ค์ผ๋ก ์ด๊ธฐํํ๊ธฐ ์ํจ) | |
| """ | |
| if not new: | |
| return old | |
| if new[0] == _STEPS_RESET_TOKEN: | |
| return new[1:] | |
| return old + new | |
| def merge_multi_answers(old: List[Dict[str, Any]], new: List[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
| """ | |
| multi_answers reducer. | |
| - ๊ธฐ๋ณธ ๋์: old + new (๋ณ๋ ฌ worker์์ ๋ต๋ณ์ ๋์์ append ๊ฐ๋ฅ) | |
| - ๋ฆฌ์ ๋์: new์ ์ฒซ ์์๊ฐ {"__token__": _MULTI_ANS_RESET_TOKEN} ์ด๋ฉด | |
| old๋ฅผ ๋ฒ๋ฆฌ๊ณ new[1:]๋ก ๊ต์ฒด | |
| (์ฒดํฌํฌ์ธํ /์ค๋ ๋ ์ ์ง๋ก ์ธํด ์ด์ ํด์ multi_answers๊ฐ ๋์ ๋๋ ๋ฌธ์ ๋ฐฉ์ง) | |
| """ | |
| if not new: | |
| return old | |
| head = new[0] | |
| if isinstance(head, dict) and head.get("__token__") == _MULTI_ANS_RESET_TOKEN: | |
| return new[1:] | |
| return old + new | |
| class SearchResult(BaseModel): | |
| """๊ฒ์ ๋๋ฉ์ธ์์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ๋ ๋จ์ผ ๊ฒ์ ๊ฒฐ๊ณผ ๋ชจ๋ธ.""" | |
| source: str = Field( | |
| ..., | |
| description="๊ฒ์ ์ถ์ฒ (์: Stack Overflow, ๊ณต์ ๋ฌธ์, GitHub Issues ๋ฑ)", | |
| ) | |
| content: str = Field( | |
| ..., | |
| description="๊ฒ์ ๊ฒฐ๊ณผ์ ํต์ฌ ๋ด์ฉ ๋๋ ๋ฐ์ท ํ ์คํธ", | |
| ) | |
| url: Optional[str] = Field( | |
| default=None, | |
| description="๊ฒ์ ๊ฒฐ๊ณผ์ ์๋ณธ ์ถ์ฒ URL (์กด์ฌํ๋ ๊ฒฝ์ฐ์๋ง ์ค์ )", | |
| ) | |
| relevance_score: Optional[float] = Field( | |
| default=None, | |
| description="๊ฒ์ ์ฟผ๋ฆฌ์์ ๊ด๋ จ๋ ์ ์ (0.0โ1.0 ๋ฒ์, ํด์๋ก ๋ ๊ด๋ จ ์์)", | |
| ) | |
| class AgentState(BaseModel): | |
| """CodeWeaver LangGraph ์์ด์ ํธ์ ์ ์ฒด ์ํ๋ฅผ ๋ํ๋ด๋ Pydantic ๋ชจ๋ธ. | |
| LangGraph ๊ณต์ ๊ฐ์ด๋๋ผ์ธ: | |
| - Pydantic BaseModel ์ฌ์ฉ (ํ์ ์์ ์ฑ) | |
| - messages ํ๋์ add_messages reducer ์ ์ฉ | |
| - ๋ชจ๋ ํ๋์ ๊ธฐ๋ณธ๊ฐ ์ ๊ณต | |
| """ | |
| # Core fields | |
| user_question: str = Field(default="", description="์ฌ์ฉ์์ ์๋ณธ ์ง๋ฌธ") | |
| messages: Annotated[List[BaseMessage], add_messages] = Field( | |
| default_factory=list, | |
| description="๋ํ ๋ฉ์์ง ํ์คํ ๋ฆฌ (add_messages reducer ์ฌ์ฉ)" | |
| ) | |
| # Legacy conversation history (์ ์งํ๋ messages ์ฐ์ ) | |
| conversation_history: Optional[List[Tuple[str, str]]] = Field( | |
| default=None, | |
| description="๋ ๊ฑฐ์ ๋ํ ๋ด์ญ (messages ์ฐ์ ์ฌ์ฉ)" | |
| ) | |
| # Intent classification | |
| detected_intent: Optional[Literal["debugging", "learning", "code_review"]] = Field( | |
| default=None, | |
| description="๋ถ๋ฅ๋ ์ง๋ฌธ ์๋" | |
| ) | |
| # Cache-related | |
| cached_result: Optional[str] = Field( | |
| default=None, | |
| description="๋ฒกํฐ DB ์บ์์์ ์กฐํ๋ ๋ต๋ณ" | |
| ) | |
| # Search results (Send API๋ฅผ ์ํ reducer ์ฌ์ฉ) | |
| search_results: Annotated[List[SearchResult], add] = Field( | |
| default_factory=list, | |
| description="๋ณ๋ ฌ ๊ฒ์์ผ๋ก ์์ง๋ ๊ฒฐ๊ณผ ๋ฆฌ์คํธ (Send API๋ก ๋ณ๋ ฌ ์ ๋ฐ์ดํธ)" | |
| ) | |
| # Intermediate processing | |
| subtask_results: Dict[str, Any] = Field( | |
| default_factory=dict, | |
| description="์๋ธํ์คํฌ ์คํ ๊ฒฐ๊ณผ ์ ์ฅ์" | |
| ) | |
| # Final output | |
| final_answer: Optional[str] = Field( | |
| default=None, | |
| description="์ต์ข ์์ฑ๋ ๋ต๋ณ" | |
| ) | |
| # Debugging/tracing (๋ณ๋ ฌ ๋ ธ๋ + ์คํ ๋จ์ ๋ฆฌ์ ์ง์ reducer ์ฌ์ฉ) | |
| intermediate_steps: Annotated[List[str], merge_intermediate_steps] = Field( | |
| default_factory=list, | |
| description="์คํ ๋จ๊ณ๋ณ ๋ก๊ทธ (๋ณ๋ ฌ ๋ ธ๋์์ ๋์ ์ ๋ฐ์ดํธ ๊ฐ๋ฅ)" | |
| ) | |
| # Question analysis & cache eligibility | |
| question_type: Optional[Literal["clarification", "new_topic", "independent"]] = Field( | |
| default=None, | |
| description="์ง๋ฌธ ์ ํ ๋ถ๋ฅ ๊ฒฐ๊ณผ" | |
| ) | |
| analysis_reasoning: Optional[str] = Field( | |
| default=None, | |
| description="์ง๋ฌธ ๋ถ์ ์ด์ " | |
| ) | |
| should_cache: Optional[bool] = Field( | |
| default=None, | |
| description="์บ์ ์ ์ฅ ์ฌ๋ถ" | |
| ) | |
| canonical_question: Optional[str] = Field( | |
| default=None, | |
| description="์ ๊ทํ๋ ์ง๋ฌธ (์บ์์ฉ)" | |
| ) | |
| # Planning & Refinement (Phase 3: Open Deep Research pattern) | |
| plan: Optional[Dict[str, Any]] = Field( | |
| default=None, | |
| description="์ง๋ฌธ ๋ถํด ๊ณํ: {'sub_questions': [...], 'reasoning': '...'}" | |
| ) | |
| needs_refinement: bool = Field( | |
| default=False, | |
| description="๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ถ์กฑํ์ฌ ์ฟผ๋ฆฌ ๊ฐ์ ํ์ ์ฌ๋ถ" | |
| ) | |
| refinement_count: int = Field( | |
| default=0, | |
| description="๊ฒ์ ์ฟผ๋ฆฌ ๊ฐ์ ์๋ ํ์ (์ต๋ 1ํ)" | |
| ) | |
| original_question: Optional[str] = Field( | |
| default=None, | |
| description="์ฟผ๋ฆฌ ๊ฐ์ ์ ์๋ณธ ์ง๋ฌธ (์ต์ข ๋ต๋ณ ์์ฑ ์ ์ฐธ์กฐ)" | |
| ) | |
| # Phase 4: Dynamic Parallel Search for Multiple Questions | |
| is_multi_question: bool = Field( | |
| default=False, | |
| description="ํ์ฌ ๋ค์ค ์ง๋ฌธ ์ฒ๋ฆฌ ์ค์ธ์ง ์ฌ๋ถ" | |
| ) | |
| sub_question_index: int = Field( | |
| default=0, | |
| description="์๋ธ ์ง๋ฌธ ์ธ๋ฑ์ค (0๋ถํฐ ์์)" | |
| ) | |
| sub_question_text: Optional[str] = Field( | |
| default=None, | |
| description="ํ์ฌ ์ฒ๋ฆฌ ์ค์ธ ์๋ธ ์ง๋ฌธ ํ ์คํธ" | |
| ) | |
| original_multi_question: Optional[str] = Field( | |
| default=None, | |
| description="๋ค์ค ์ง๋ฌธ์ ์๋ณธ ์ง๋ฌธ (ํตํฉ ๋ต๋ณ ์์ฑ ์ ์ฐธ์กฐ)" | |
| ) | |
| multi_answers: Annotated[List[Dict[str, Any]], merge_multi_answers] = Field( | |
| default_factory=list, | |
| description="๋ค์ค ์ง๋ฌธ์ ๊ฐ ๋ต๋ณ ๋ฆฌ์คํธ (reducer๋ก ์๋ ๋ณํฉ)" | |
| ) | |
| class Config: | |
| arbitrary_types_allowed = True |