Sharath1036 commited on
Commit
4a8958c
·
1 Parent(s): 1df19b1

first commit

Browse files
Files changed (7) hide show
  1. .github/workflows/cicd.yml +20 -0
  2. .gitignore +2 -0
  3. node.py +116 -0
  4. pdf_agent.py +102 -0
  5. requirements.txt +15 -3
  6. src/streamlit_app.py +90 -33
  7. weather_agent.py +62 -0
.github/workflows/cicd.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ push:
4
+ branches: [main]
5
+
6
+ # to run this workflow manually from the Actions tab
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ sync-to-hub:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+ with:
15
+ fetch-depth: 0
16
+ lfs: true
17
+ - name: Push to hub
18
+ env:
19
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
+ run: git push --force https://Sharath1036:$HF_TOKEN@huggingface.co/spaces/Sharath1036/langchain-multi-agents main
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ venv/*
2
+ .env
node.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Literal
2
+ from langchain_core.messages import BaseMessage, HumanMessage
3
+ from langgraph.prebuilt import create_react_agent
4
+ from langgraph.graph import MessagesState, END
5
+ from langgraph.types import Command
6
+ from weather_agent import WeatherAgent
7
+ from pdf_agent import PDFAgent
8
+ from langgraph.graph import StateGraph, START
9
+ from IPython.display import Image, display
10
+ import re
11
+
12
+ def split_questions(user_message: str) -> List[str]:
13
+ # Naive split on ' and ', ' then ', case insensitive
14
+ parts = re.split(r'\band then\b|\band\b|\bthen\b', user_message, flags=re.IGNORECASE)
15
+ return [part.strip() for part in parts if part.strip()]
16
+
17
+ def classify_question(question: str) -> Literal["pdf_agent", "weather_agent"]:
18
+ # Simple keyword-based classification
19
+ if re.search(r'\bweather\b', question, re.IGNORECASE):
20
+ return "weather_agent"
21
+ else:
22
+ return "pdf_agent"
23
+
24
+ def pdf_agent_node(state: MessagesState) -> Command[Literal["weather_agent", END]]:
25
+ pdf_agent = PDFAgent(pdf_path="Sharath_OnePage.pdf")
26
+ user_message = None
27
+ for message in reversed(state["messages"]):
28
+ if isinstance(message, HumanMessage):
29
+ user_message = message.content
30
+ break
31
+ if user_message is None:
32
+ raise ValueError("No user message found in state.")
33
+
34
+ result = pdf_agent.agent.invoke({"input": user_message})
35
+ # Extract string from result
36
+ if isinstance(result, dict):
37
+ # Try common keys
38
+ text_result = result.get("output") or result.get("text") or str(result)
39
+ else:
40
+ text_result = str(result)
41
+
42
+ final_msg = HumanMessage(content=text_result, name="pdf_agent")
43
+ goto = get_next_node(final_msg, "weather_agent")
44
+ return Command(
45
+ update={"messages": state["messages"] + [final_msg]},
46
+ goto=goto,
47
+ )
48
+
49
+ def weather_agent_node(state: MessagesState) -> Command[Literal["pdf_agent", END]]:
50
+ weather_agent = WeatherAgent()
51
+ user_message = None
52
+ for message in reversed(state["messages"]):
53
+ if isinstance(message, HumanMessage):
54
+ user_message = message.content
55
+ break
56
+ if user_message is None:
57
+ raise ValueError("No user message found in state.")
58
+
59
+ match = re.search(r"weather in ([\w\s,]+)", user_message, re.IGNORECASE)
60
+ location = match.group(1).strip() if match else user_message
61
+ result = weather_agent.ask(location)
62
+ final_msg = HumanMessage(content=result, name="weather_agent")
63
+ goto = get_next_node(final_msg, "pdf_agent")
64
+ return Command(
65
+ update={"messages": state["messages"] + [final_msg]},
66
+ goto=goto,
67
+ )
68
+
69
+ def get_next_node(last_message: BaseMessage, goto: str):
70
+ if "FINAL ANSWER" in last_message.content:
71
+ return END
72
+ return goto
73
+
74
+ def build_graph():
75
+ workflow = StateGraph(MessagesState)
76
+ workflow.add_node("pdf_agent", pdf_agent_node)
77
+ workflow.add_node("weather_agent", weather_agent_node)
78
+
79
+ workflow.add_edge(START, "pdf_agent")
80
+ workflow.add_edge("pdf_agent", "weather_agent")
81
+ workflow.add_edge("weather_agent", END)
82
+
83
+ graph = workflow.compile()
84
+ return graph
85
+
86
+ if __name__ == "__main__":
87
+ graph = build_graph()
88
+ display(Image(graph.get_graph().draw_mermaid_png()))
89
+
90
+ # Full user input with multiple questions
91
+ user_input = "What organizations has Sharath worked for and tell me the weather in Mumbai"
92
+
93
+ # Split into sub-questions
94
+ questions = split_questions(user_input)
95
+
96
+ # Prepare empty message list to accumulate conversation
97
+ messages = []
98
+
99
+ # Process each question routed to the correct agent node
100
+ for question in questions:
101
+ agent_name = classify_question(question)
102
+ # Run the corresponding node manually with current messages + new question
103
+ state = {"messages": messages + [HumanMessage(content=question)]}
104
+ if agent_name == "pdf_agent":
105
+ cmd = pdf_agent_node(state)
106
+ else:
107
+ cmd = weather_agent_node(state)
108
+
109
+ # Update messages with agent response
110
+ messages = cmd.update["messages"]
111
+
112
+ # Print all agent responses
113
+ for msg in messages:
114
+ if not isinstance(msg, HumanMessage):
115
+ continue
116
+ print(f"{msg.name or 'user'}: {msg.content}")
pdf_agent.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from langchain.agents import AgentType, Tool, initialize_agent
4
+ from langchain_community.agent_toolkits.load_tools import load_tools
5
+ from langchain.chains import RetrievalQA
6
+ from langchain_google_genai import GoogleGenerativeAIEmbeddings
7
+ from langchain_google_genai import ChatGoogleGenerativeAI
8
+ from langchain.text_splitter import CharacterTextSplitter
9
+ from langchain_qdrant import QdrantVectorStore
10
+ from qdrant_client import QdrantClient
11
+ from langchain_community.document_loaders import PyPDFLoader
12
+
13
+
14
+ class PDFAgent:
15
+ def __init__(self, pdf_path: str, collection_name: str = "test"):
16
+ self.pdf_path = pdf_path
17
+ self.collection_name = collection_name
18
+ self._load_environment()
19
+ self.llm = self._initialize_llm()
20
+ self.embeddings = self._initialize_embeddings()
21
+ self.vector_store = self._initialize_vector_store()
22
+ self.qa_chain = self._initialize_qa_chain()
23
+ self.tools = self._initialize_tools()
24
+ self.agent = self._initialize_agent()
25
+
26
+ def _load_environment(self):
27
+ load_dotenv(override=True)
28
+ os.environ["GOOGLE_API_KEY"] = os.getenv("GOOGLE_API_KEY")
29
+ os.environ["QDRANT_API_KEY"] = os.getenv("QDRANT_API_KEY")
30
+ os.environ["QDRANT_URL"] = os.getenv("QDRANT_URL")
31
+ os.environ["LANGSMITH_TRACING"]= "true"
32
+ os.environ["LANGSMITH_API_KEY"] = os.getenv("LANGSMITH_API_KEY")
33
+
34
+ def _initialize_llm(self):
35
+ return ChatGoogleGenerativeAI(
36
+ model="gemini-2.5-flash",
37
+ api_key=os.getenv("GOOGLE_API_KEY"),
38
+ temperature=0.0,
39
+ )
40
+
41
+ def _initialize_embeddings(self):
42
+ return GoogleGenerativeAIEmbeddings(model="gemini-embedding-001")
43
+
44
+ def _initialize_vector_store(self):
45
+ loader = PyPDFLoader(self.pdf_path)
46
+ documents = loader.load()
47
+
48
+ text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
49
+ split_texts = text_splitter.split_documents(documents)
50
+
51
+ return QdrantVectorStore.from_documents(
52
+ documents=split_texts,
53
+ embedding=self.embeddings,
54
+ collection_name=self.collection_name,
55
+ api_key=os.getenv("QDRANT_API_KEY"),
56
+ url=os.getenv("QDRANT_URL"),
57
+ force_recreate=True
58
+ )
59
+
60
+ def _initialize_qa_chain(self):
61
+ return RetrievalQA.from_chain_type(
62
+ llm=self.llm,
63
+ chain_type="stuff",
64
+ retriever=self.vector_store.as_retriever()
65
+ )
66
+
67
+ def _initialize_tools(self):
68
+ tools = load_tools([], llm=self.llm)
69
+ tools.append(
70
+ Tool(
71
+ name="State of Union QA System",
72
+ func=self.qa_chain.run,
73
+ description=(
74
+ "Useful for answering questions from the uploaded PDF. "
75
+ "Input should be a fully formed question."
76
+ ),
77
+ )
78
+ )
79
+ return tools
80
+
81
+ def _initialize_agent(self):
82
+ return initialize_agent(
83
+ self.tools,
84
+ self.llm,
85
+ agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
86
+ verbose=True,
87
+ )
88
+
89
+ def ask(self, question: str):
90
+ print("Asking:", question)
91
+ result = self.agent.run(question)
92
+ print("Result:", result)
93
+ return result
94
+
95
+
96
+
97
+ if __name__ == "__main__":
98
+ print("Starting PDF Agent...")
99
+ pdf_agent = PDFAgent(pdf_path="Sharath_OnePage.pdf")
100
+ print("Agent initialized.")
101
+ response = pdf_agent.ask("What all organizations has Sharath worked with?")
102
+ print("Response:", response)
requirements.txt CHANGED
@@ -1,3 +1,15 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ langchain
2
+ langchain-community
3
+ langchain-openai
4
+ langgraph
5
+ pyowm
6
+ python-dotenv
7
+ pypdf
8
+ langchain-qdrant
9
+ qdrant-client
10
+ requests
11
+ ipython
12
+ langsmith
13
+ openai
14
+ streamlit
15
+ langchain_google_genai
src/streamlit_app.py CHANGED
@@ -1,40 +1,97 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
 
 
 
8
 
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
 
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
 
 
2
  import streamlit as st
3
+ import tempfile
4
+ from pdf_agent import PDFAgent
5
+ from weather_agent import WeatherAgent
6
 
7
+ # Ensure an event loop exists for async libraries (fix for Google Generative AI Embeddings)
8
+ try:
9
+ asyncio.get_event_loop()
10
+ except RuntimeError:
11
+ asyncio.set_event_loop(asyncio.new_event_loop())
12
 
13
+ st.set_page_config(page_title="LangGraph Agents Demo", layout="wide")
14
+ st.title("LangGraph Agents Demo")
 
15
 
16
+ tab1, tab2, tab3 = st.tabs(["PDF Agent", "Weather Agent", "Multi-Agent QA"])
 
17
 
18
+ with tab1:
19
+ st.header("PDF Agent")
20
+ uploaded_pdf = st.file_uploader("Upload a PDF", type=["pdf"])
21
+ question = st.text_input("Ask a question about the PDF:")
22
+ if uploaded_pdf and question:
23
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
24
+ tmp_file.write(uploaded_pdf.read())
25
+ tmp_path = tmp_file.name
26
+ pdf_agent = PDFAgent(pdf_path=tmp_path)
27
+ with st.spinner("Processing..."):
28
+ answer = pdf_agent.ask(question)
29
+ st.success("Answer:")
30
+ st.write(answer)
31
 
32
+ with tab2:
33
+ st.header("Weather Agent")
34
+ location = st.text_input("Enter a location for weather info: e.g. Mumbai")
35
+ if location:
36
+ weather_agent = WeatherAgent()
37
+ with st.spinner("Fetching weather..."):
38
+ try:
39
+ result = weather_agent.ask(location)
40
+ st.success("Weather Info:")
41
+ st.write(result) # This might be None or a dict
42
+ # Try to extract the answer if it's a dict or object
43
+ # if isinstance(result, dict):
44
+ # # Try common keys
45
+ # if "output" in result:
46
+ # st.write(result["output"])
47
+ # elif "result" in result:
48
+ # st.write(result["result"])
49
+ # else:
50
+ # st.write(str(result))
51
+ # elif hasattr(result, "content"):
52
+ # st.write(result.content)
53
+ # elif result is not None:
54
+ # st.write(str(result))
55
+ except Exception as e:
56
+ st.error(f"Error: {e}")
57
 
58
+ with tab3:
59
+ st.header("Multi-Agent QA (PDF + Weather)")
60
+ user_input = st.text_area("Ask multiple questions (e.g. 'What organizations has Sharath worked for and tell me the weather in Mumbai'):")
61
+ uploaded_pdf = st.file_uploader("Upload a PDF for PDF Agent (optional)", type=["pdf"], key="multi_pdf")
62
+ if st.button("Ask Multi-Agent"):
63
+ from node import split_questions, classify_question
64
+ from langchain_core.messages import HumanMessage
65
+ import tempfile
66
+ messages = []
67
+ # If PDF uploaded, save and use it
68
+ pdf_path = None
69
+ if uploaded_pdf:
70
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
71
+ tmp_file.write(uploaded_pdf.read())
72
+ pdf_path = tmp_file.name
73
+ # Split and process each question
74
+ questions = split_questions(user_input)
75
+ for question in questions:
76
+ agent_name = classify_question(question)
77
+ if agent_name == "pdf_agent":
78
+ if pdf_path:
79
+ pdf_agent = PDFAgent(pdf_path=pdf_path)
80
+ else:
81
+ pdf_agent = PDFAgent(pdf_path="Sharath_OnePage.pdf")
82
+ result = pdf_agent.agent.invoke({"input": question})
83
+ if isinstance(result, dict):
84
+ text_result = result.get("output") or result.get("text") or str(result)
85
+ else:
86
+ text_result = str(result)
87
+ messages.append(("PDF Agent", text_result))
88
+ else:
89
+ weather_agent = WeatherAgent()
90
+ import re
91
+ match = re.search(r"weather in ([\w\s,]+)", question, re.IGNORECASE)
92
+ location = match.group(1).strip() if match else question
93
+ result = weather_agent.ask(location)
94
+ messages.append(("Weather Agent", str(result)))
95
+ st.subheader("Results:")
96
+ for agent, answer in messages:
97
+ st.markdown(f"**{agent}:** {answer}")
weather_agent.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from langchain_community.utilities import OpenWeatherMapAPIWrapper
3
+ from dotenv import load_dotenv
4
+ from langchain_google_genai import ChatGoogleGenerativeAI
5
+ from langchain.agents import AgentType, Tool, initialize_agent
6
+
7
+ class WeatherAgent:
8
+ def __init__(self):
9
+ self._load_environment()
10
+ self.weather_tool = self._initialize_weather_tool()
11
+ self.llm = self._initialize_llm()
12
+ self.tools = self._initialize_tools()
13
+ self.agent = self._initialize_agent()
14
+
15
+ def _load_environment(self):
16
+ load_dotenv(override=True)
17
+ os.environ["GOOGLE_API_KEY"] = os.getenv("GOOGLE_API_KEY")
18
+ os.environ["OPENWEATHERMAP_API_KEY"] = os.getenv("OPENWEATHERMAP_API_KEY")
19
+ os.environ["LANGSMITH_TRACING"]= "true"
20
+ os.environ["LANGSMITH_API_KEY"] = os.getenv("LANGSMITH_API_KEY")
21
+
22
+ def _initialize_weather_tool(self):
23
+ return OpenWeatherMapAPIWrapper()
24
+
25
+ def _initialize_llm(self):
26
+ return ChatGoogleGenerativeAI(
27
+ model="gemini-2.5-flash",
28
+ api_key=os.getenv("GOOGLE_API_KEY"),
29
+ temperature=0.0,
30
+ )
31
+
32
+ def _initialize_tools(self):
33
+ return [
34
+ Tool(
35
+ name="weather",
36
+ func=self.weather_tool.run,
37
+ description="Use this tool to get the current weather in a specified location."
38
+ )
39
+ ]
40
+
41
+ def _initialize_agent(self):
42
+ return initialize_agent(
43
+ self.tools,
44
+ self.llm,
45
+ agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
46
+ verbose=True,
47
+ )
48
+
49
+ def ask(self, location: str):
50
+ prompt = f"What's the weather like in {location}?"
51
+ print("Asking:", prompt)
52
+ result = self.agent.run(prompt)
53
+ print("Result:", result)
54
+ return result
55
+
56
+ if __name__ == "__main__":
57
+ print("Starting Weather Agent...")
58
+ weather_agent = WeatherAgent()
59
+ print("Agent initialized.")
60
+ location = "Avignon" # Example location
61
+ response = weather_agent.ask(location)
62
+ print("Response:", response)