Project Background
I've been playing with LangChain ever since it came out, and one thing led to another — I ended up contributing to a book on LangChain & LangGraph.
Why It Was Needed
When you build an agent with LangChain 1.0.0+ (LangGraph), it executes through multiple nodes internally. I wanted to display that process in real time inside a Streamlit UI.
The code got complicated enough that I packaged it as a library for readers who are new to the stack. Link
StreamlitLanggraphHandler
sequenceDiagram
participant U as 사용자
participant S as Streamlit UI
participant H as Handler
participant G as LangGraph Agent
U->>S: 질문 입력
S->>H: 핸들러 시작
H->>G: 에이전트 실행
G-->>H: Thought 스트림
H-->>S: Thought 표시
G-->>H: Action 스트림
H-->>S: Action 표시
G-->>H: Final Answer
H-->>S: 최종 답변 표시
Version History
v0.1.0: Initial release - basic handler
v0.1.1: Added user-friendly error handling
v0.1.2: Improved error messages
v0.1.4: Dedicated placeholder for Thought management
v0.2.0: Expanded configurable properties
v0.2.1: Improved Thought placeholder rendering
v0.2.3: Handle diverse LLM content formats
v0.3.0: LangSmith integration
v0.3.1: Added Run ID capture handler
v0.3.2: Refactored LangSmith feedback collection
v0.3.3: Refactored to inherit from BaseCallbackHandler
LangSmith Integration
v0.3.0 integrated LangSmith to enable agent run tracing and feedback collection:
handler = StreamlitLanggraphHandler(
langsmith_enabled=True,
langsmith_project="my-agent"
)
# 사용자가 답변에 피드백을 남기면
# LangSmith에 자동으로 기록됨
Text Extraction Method
Different LLMs return responses in different formats. The extract_text method provides a unified way to handle them:
def extract_text(content) -> str:
if isinstance(content, str):
return content
if isinstance(content, list):
return " ".join(extract_text(c) for c in content)
if hasattr(content, "text"):
return content.text
return str(content)