| import streamlit as st |
| from typing import TypedDict, List |
| from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint |
| from langchain_core.prompts import ChatPromptTemplate |
| from langchain_community.tools import DuckDuckGoSearchRun |
| from langgraph.graph import StateGraph, END |
|
|
| |
| class AgentState(TypedDict): |
| question: str |
| context: str |
| answer: str |
| trace: List[str] |
|
|
| |
| def get_llm(token: str): |
| """Initializes the LLM using the token provided in the UI.""" |
| try: |
| llm = HuggingFaceEndpoint( |
| repo_id="Qwen/Qwen2.5-7B-Instruct", |
| task="text-generation", |
| max_new_tokens=512, |
| huggingfacehub_api_token=token, |
| ) |
| return ChatHuggingFace(llm=llm, token=token) |
| except Exception as e: |
| st.error(f"Failed to initialize model: {str(e)}") |
| return None |
|
|
| |
| def router_node(state: AgentState, token: str): |
| llm = get_llm(token) |
| prompt = ChatPromptTemplate.from_template( |
| "Determine if this needs a web search for real-time info. " |
| "Reply ONLY with 'SEARCH' or 'KNOWLEDGE'.\nQuestion: {question}" |
| ) |
| chain = prompt | llm |
| res = chain.invoke({"question": state["question"]}) |
| decision = "SEARCH" if "SEARCH" in res.content.upper() else "KNOWLEDGE" |
| return {"context": decision, "trace": [f"π Router: Selected {decision} path"]} |
|
|
| def search_node(state: AgentState): |
| search = DuckDuckGoSearchRun() |
| results = search.run(state["question"]) |
| return {"context": results, "trace": state["trace"] + ["π Web Search: Results gathered"]} |
|
|
| def writer_node(state: AgentState, token: str): |
| llm = get_llm(token) |
| prompt = ChatPromptTemplate.from_template( |
| "Context: {context}\n\nProvide a detailed answer to: {question}" |
| ) |
| chain = prompt | llm |
| res = chain.invoke({"context": state["context"], "question": state["question"]}) |
| return {"answer": res.content, "trace": state["trace"] + ["βοΈ Writer: Generated final response"]} |
|
|
| |
| st.set_page_config(page_title="LangChain Advanced Practice", layout="wide") |
|
|
| st.title("ποΈ LangGraph Practice Lab") |
| st.markdown("Build and test advanced autonomous workflows using open-source models.") |
|
|
| |
| with st.sidebar: |
| st.header("Authentication") |
| user_token = st.text_input("Enter Hugging Face Token (hf_...)", type="password") |
| st.info("Get your token at: [hf.co/settings/tokens](https://huggingface.co/settings/tokens)") |
| |
| if user_token: |
| st.success("Token provided!") |
| else: |
| st.warning("Please enter a token to unlock the agent.") |
|
|
| |
| if user_token: |
| query = st.text_input("Task: Ask a question that requires reasoning or search") |
| |
| if st.button("Execute Agentic Workflow"): |
| if query: |
| |
| workflow = StateGraph(AgentState) |
| |
| |
| workflow.add_node("router", lambda x: router_node(x, user_token)) |
| workflow.add_node("search", search_node) |
| workflow.add_node("writer", lambda x: writer_node(x, user_token)) |
| |
| |
| workflow.set_entry_point("router") |
| workflow.add_conditional_edges( |
| "router", |
| lambda x: "web" if x["context"] == "SEARCH" else "direct", |
| {"web": "search", "direct": "writer"} |
| ) |
| workflow.add_edge("search", "writer") |
| workflow.add_edge("writer", END) |
| |
| app = workflow.compile() |
| |
| |
| with st.status("Agent Orchestrating...", expanded=True) as status: |
| final_state = app.invoke({"question": query, "trace": []}) |
| |
| st.subheader("Process Trace") |
| for step in final_state["trace"]: |
| st.write(step) |
| |
| st.subheader("Final Output") |
| st.write(final_state["answer"]) |
| status.update(label="Task Finished!", state="complete") |
| else: |
| st.error("Enter a query first.") |
| else: |
| st.info("π Enter your Hugging Face token in the sidebar to start practicing.") |
|
|
| |
| st.divider() |
| with st.expander("π Advanced Level Learning Goals"): |
| st.markdown(""" |
| - **LCEL Implementation**: Watch how the `prompt | llm` chain is constructed. |
| - **State Control**: Observe how `AgentState` passes data between nodes. |
| - **Conditional Routing**: The router node uses the LLM as a logic gate. |
| - **Tool Integration**: DuckDuckGo search is used as a dynamic external tool. |
| """) |