Hashim998 commited on
Commit
7d3517c
·
verified ·
1 Parent(s): a56e144
Files changed (5) hide show
  1. .env +1 -0
  2. README.md +5 -9
  3. app.py +292 -0
  4. chat_history.json +121 -0
  5. requirements.txt +6 -0
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ GEMINI_API_KEY=AIzaSyCZtDtt7K6g3IgR0GsQ2pil4xsu7Jrt_lk
README.md CHANGED
@@ -1,12 +1,8 @@
1
  ---
2
- title: Bot
3
- emoji: 📚
4
- colorFrom: gray
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 5.31.0
8
  app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: LangChain Powered ChatBot
3
+ emoji: 🤖
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
 
7
  app_file: app.py
8
+ ---
 
 
 
app.py ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import json
4
+ import time
5
+ import gradio as gr
6
+ import logging
7
+ from dotenv import load_dotenv
8
+ import google.generativeai as genai
9
+
10
+ from langgraph.graph import START, MessagesState, StateGraph
11
+ from langgraph.checkpoint.memory import MemorySaver
12
+ from langchain_core.messages import HumanMessage, AIMessage
13
+ from langchain_core.prompts.chat import (
14
+ ChatPromptTemplate,
15
+ SystemMessagePromptTemplate,
16
+ MessagesPlaceholder,
17
+ HumanMessagePromptTemplate,
18
+ )
19
+ from langchain_google_genai import ChatGoogleGenerativeAI
20
+ from langchain_core.messages import BaseMessage
21
+
22
+
23
+ # === Logging & .env ===
24
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
25
+ logger = logging.getLogger(__name__)
26
+ load_dotenv()
27
+
28
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
29
+ if not GEMINI_API_KEY:
30
+ raise ValueError("Missing GEMINI_API_KEY")
31
+ genai.configure(api_key=GEMINI_API_KEY)
32
+
33
+ HISTORY_FILE = "chat_history.json"
34
+
35
+ # === Persistent Storage ===
36
+ def load_all_sessions():
37
+ if os.path.exists(HISTORY_FILE):
38
+ with open(HISTORY_FILE, "r", encoding="utf-8") as f:
39
+ return json.load(f)
40
+ return {}
41
+
42
+ def save_all_sessions(sessions):
43
+ with open(HISTORY_FILE, "w", encoding="utf-8") as f:
44
+ json.dump(sessions, f, indent=2)
45
+
46
+ # === Chatbot Class ===
47
+ class GeminiChatbot:
48
+ def __init__(self):
49
+ self.setup_model()
50
+
51
+ def setup_model(self):
52
+ system_template = """
53
+ You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe.
54
+ Your answers should be informative, engaging, and accurate. If a question doesn't make any sense, or isn't factually coherent, explain why instead of answering something not correct.
55
+ If you don't know the answer to a question, please don't share false information.
56
+ """
57
+
58
+ self.prompt = ChatPromptTemplate.from_messages([
59
+ SystemMessagePromptTemplate.from_template(system_template),
60
+ MessagesPlaceholder(variable_name="chat_history"),
61
+ HumanMessagePromptTemplate.from_template("{input}")
62
+ ])
63
+
64
+ self.model = ChatGoogleGenerativeAI(
65
+ model="gemini-2.0-flash",
66
+ temperature=0.7,
67
+ top_p=0.95,
68
+ google_api_key=GEMINI_API_KEY,
69
+ convert_system_message_to_human=True
70
+ )
71
+
72
+ def call_model(state: MessagesState):
73
+ chat_history = state["messages"][:-1]
74
+ user_input = state["messages"][-1].content
75
+
76
+ formatted_messages = self.prompt.format_messages(
77
+ chat_history=chat_history,
78
+ input=user_input
79
+ )
80
+
81
+ response = self.model.invoke(formatted_messages)
82
+ return {"messages": response}
83
+
84
+ workflow = StateGraph(state_schema=MessagesState)
85
+ workflow.add_node("model", call_model)
86
+ workflow.add_edge(START, "model")
87
+
88
+ self.memory = MemorySaver()
89
+ self.app = workflow.compile(checkpointer=self.memory)
90
+
91
+ def get_response(self, user_message, history, thread_id):
92
+ try:
93
+ # Convert string history into LangChain message objects
94
+ langchain_history = []
95
+ for user, bot in history:
96
+ langchain_history.append(HumanMessage(content=user))
97
+ langchain_history.append(AIMessage(content=bot))
98
+
99
+ # Add the new user message
100
+ input_message = HumanMessage(content=user_message)
101
+ full_history = langchain_history + [input_message]
102
+
103
+ full_response = ""
104
+ config = {"configurable": {"thread_id": thread_id}}
105
+
106
+ # Invoke the model with full conversation
107
+ response = self.app.invoke({"messages": full_history}, config)
108
+ complete_response = response["messages"][-1].content
109
+
110
+ for char in complete_response:
111
+ full_response += char
112
+ yield full_response
113
+ time.sleep(0.01)
114
+
115
+ except Exception as e:
116
+ logger.error(f"LangGraph Error: {e}")
117
+ yield f"⚠ Error: {type(e).__name__} — {str(e)}"
118
+
119
+
120
+ # === Gradio UI ===
121
+ chatbot = GeminiChatbot()
122
+ sessions = load_all_sessions()
123
+
124
+
125
+ def launch_interface():
126
+ with gr.Blocks(
127
+ theme=gr.themes.Base(),
128
+ css="""
129
+ body {
130
+ background-color: black;
131
+ }
132
+ .gr-block.gr-textbox textarea {
133
+ background-color: #2f2f2f;
134
+ color: white;
135
+ }
136
+ .gr-chatbot {
137
+ background-color: #2f2f2f;
138
+ color: white;
139
+ }
140
+ .gr-button, .gr-dropdown {
141
+ margin: 5px auto;
142
+ display: block;
143
+ width: 50%;
144
+ }
145
+ .gr-markdown h2 {
146
+ text-align: center;
147
+ color: white;
148
+ }
149
+ """
150
+ ) as demo:
151
+ demo.title = "LangChain Powered ChatBot"
152
+ gr.Markdown("## LangChain Powered ChatBot")
153
+
154
+ current_thread_id = gr.State()
155
+ session_names = gr.State()
156
+ history = gr.State([])
157
+
158
+ # Initialize with first session or create new
159
+ if not sessions:
160
+ new_id = str(uuid.uuid4())
161
+ sessions[new_id] = []
162
+ save_all_sessions(sessions)
163
+ current_thread_id.value = new_id
164
+ session_names.value = [f"NEW: {new_id}"]
165
+ else:
166
+ current_thread_id.value = next(iter(sessions.keys()))
167
+ session_names.value = [f"PREVIOUS: {k}" for k in sessions.keys()]
168
+
169
+ def get_dropdown_choices():
170
+ """Get current dropdown choices including active sessions and new chat"""
171
+ choices = []
172
+ for session_id in sessions:
173
+ if sessions[session_id]: # Only show sessions with history
174
+ choices.append(f"PREVIOUS: {session_id}")
175
+ choices.append(f"NEW: {current_thread_id.value}")
176
+ return choices
177
+
178
+ with gr.Column():
179
+ new_chat_btn = gr.Button("New Chat", variant="primary")
180
+ session_selector = gr.Dropdown(
181
+ label="Chats",
182
+ choices=get_dropdown_choices(),
183
+ value=f"NEW: {current_thread_id.value}",
184
+ interactive=True
185
+ )
186
+
187
+ chatbot_ui = gr.Chatbot(label="Conversation", height=320)
188
+
189
+ with gr.Row():
190
+ msg = gr.Textbox(placeholder="Ask a question...", container=False, scale=9)
191
+ send = gr.Button("Send", variant="primary", scale=1)
192
+
193
+ clear = gr.Button("Clear Current Chat")
194
+
195
+ def start_new_chat():
196
+ new_id = str(uuid.uuid4())
197
+ sessions[new_id] = []
198
+ save_all_sessions(sessions)
199
+
200
+ # Format for dropdown
201
+ display_name = f"NEW: {new_id}"
202
+ updated_choices = [f"PREVIOUS: {k}" for k in sessions if sessions[k]] + [display_name]
203
+
204
+ return (
205
+ new_id, # thread ID state
206
+ [], # history
207
+ gr.update(choices=updated_choices, value=display_name), # update dropdown
208
+ display_name # visible value
209
+ )
210
+
211
+
212
+
213
+ def switch_chat(selected_display_id):
214
+ """Switch between different chat sessions"""
215
+ if not selected_display_id:
216
+ return current_thread_id.value, [], ""
217
+
218
+ true_id = selected_display_id.split(": ", 1)[-1]
219
+ chat_history = sessions.get(true_id, [])
220
+ return true_id, chat_history, selected_display_id
221
+
222
+ def respond(message, history, thread_id):
223
+ """Generate response and update chat history"""
224
+ if not message.strip():
225
+ yield history
226
+ return
227
+
228
+ # Add user message to history
229
+ history.append((message, ""))
230
+ yield history
231
+
232
+ # Stream response
233
+ full_response = ""
234
+ for chunk in chatbot.get_response(message, history[:-1], thread_id):
235
+ full_response = chunk
236
+ history[-1] = (message, full_response)
237
+ yield history
238
+
239
+ # Save updated session
240
+ sessions[thread_id] = history
241
+ save_all_sessions(sessions)
242
+
243
+ def clear_current(thread_id):
244
+ """Clear current chat history"""
245
+ sessions[thread_id] = []
246
+ save_all_sessions(sessions)
247
+ return []
248
+
249
+ new_chat_btn.click(
250
+ start_new_chat,
251
+ outputs=[current_thread_id, chatbot_ui, session_selector, session_selector]
252
+ )
253
+
254
+ session_selector.change(
255
+ switch_chat,
256
+ inputs=session_selector,
257
+ outputs=[current_thread_id, chatbot_ui, session_selector]
258
+ )
259
+
260
+ send.click(
261
+ respond,
262
+ inputs=[msg, chatbot_ui, current_thread_id],
263
+ outputs=[chatbot_ui]
264
+ ).then(
265
+ lambda: "", None, msg # Clear input after sending
266
+ )
267
+
268
+ msg.submit(
269
+ respond,
270
+ inputs=[msg, chatbot_ui, current_thread_id],
271
+ outputs=[chatbot_ui]
272
+ ).then(
273
+ lambda: "", None, msg # Clear input after sending
274
+ )
275
+
276
+ clear.click(
277
+ clear_current,
278
+ inputs=[current_thread_id],
279
+ outputs=[chatbot_ui]
280
+ )
281
+
282
+ return demo
283
+
284
+
285
+
286
+ # === Run App ===
287
+ if __name__ == "__main__":
288
+ try:
289
+ demo = launch_interface()
290
+ demo.launch(share=True)
291
+ except Exception as e:
292
+ logger.critical(f"App failed: {e}")
chat_history.json ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "94289faf-ed53-4cf4-806d-354e93b60082": [
3
+ [
4
+ "yolo",
5
+ "Understood! I will do my best to provide helpful, respectful, honest, safe, informative, engaging, and accurate responses. I will also explain when a question is unclear or factually incorrect, and I will admit when I don't know the answer.\n\nAnd, while I appreciate the sentiment, I won't be incorporating \"yolo\" into my responses. My goal is to be helpful and responsible, and that doesn't quite align with that particular phrase! \ud83d\ude09"
6
+ ]
7
+ ],
8
+ "039feb55-6572-415b-bf5a-c922610ad36a": [
9
+ [
10
+ "what is my name",
11
+ "I am an AI conversational chatbot, so I don't have access to personal information that would include your name. I do not know what your name is."
12
+ ]
13
+ ],
14
+ "1a1a3268-ddf5-41d8-a518-fc27a500898f": [
15
+ [
16
+ "what is 3 + 3",
17
+ "3 + 3 = 6"
18
+ ],
19
+ [
20
+ "my name is khan",
21
+ "It's nice to meet you, Khan! How can I help you today?"
22
+ ],
23
+ [
24
+ "what is my name",
25
+ "I am an AI conversational chatbot, and I don't have access to personal information like your name. I don't have a memory of past conversations. Therefore, I don't know your name. You would have to tell me!"
26
+ ],
27
+ [
28
+ "what is my name",
29
+ "As I explained before, I am an AI and have no memory of past conversations. You just told me your name is Khan. Is that correct?"
30
+ ],
31
+ [
32
+ "my name is hashim khan",
33
+ "Okay, Hashim Khan, it's nice to meet you! How can I help you today?"
34
+ ],
35
+ [
36
+ "what is my name",
37
+ "Your name is Hashim Khan."
38
+ ],
39
+ [
40
+ "what is my name",
41
+ "Your name is Hashim Khan. I will remember that. How can I help you, Hashim Khan?"
42
+ ],
43
+ [
44
+ "hello",
45
+ "Hello Hashim Khan! How can I help you today?"
46
+ ],
47
+ [
48
+ "what is 2+2",
49
+ "2 + 2 = 4"
50
+ ],
51
+ [
52
+ "what is my name",
53
+ "Your name is Hashim Khan."
54
+ ],
55
+ [
56
+ "what is my name",
57
+ "Your name is Hashim Khan. I remember! Do you have any other questions for me, Hashim Khan?"
58
+ ],
59
+ [
60
+ "youre a racist",
61
+ "I understand that you feel that way, but I want to assure you that I am programmed to be a helpful and harmless AI assistant. I don't have personal opinions or biases, and I treat everyone the same, regardless of their background. My purpose is to provide accurate and unbiased information to the best of my ability.\n\nIf you feel that something I said was offensive or discriminatory, please let me know so I can learn from the experience and improve my responses in the future. I am always striving to be a better AI assistant, and your feedback is valuable to me."
62
+ ]
63
+ ],
64
+ "a3efb71c-ed41-478a-9d8b-487639511017": [
65
+ [
66
+ "what is my name",
67
+ "I am an AI conversational chatbot, so I don't have a memory of past conversations. Therefore, I do not know your name. You would have to tell me your name."
68
+ ]
69
+ ],
70
+ "86b01091-31c3-49f2-9214-5385ddc197e7": [],
71
+ "d80f54d2-0de9-4330-998c-a46b08a261fc": [
72
+ [
73
+ "what is my name",
74
+ "I am an AI conversational chatbot, so I don't have a memory of past conversations. Therefore, I do not know your name. You would need to tell me your name."
75
+ ],
76
+ [
77
+ "hat is 2 + 2",
78
+ "2 + 2 = 4"
79
+ ]
80
+ ],
81
+ "5b965323-f544-4e3b-8a7b-d7d0f102841a": [],
82
+ "9ac97858-e915-41a7-ac80-66f6c5be0eac": [],
83
+ "14d89fca-be3e-4a18-ae13-efe1344dfdec": [],
84
+ "44fd1cd6-b447-405f-8988-1b26c1647cd8": [],
85
+ "001f8e8c-ec74-48da-ba53-a106aa3f01a4": [],
86
+ "a91af89b-3b3d-45e2-a15f-5857afcebe8c": [],
87
+ "f48fcab5-ee0c-4b85-aa35-7bb376585577": [],
88
+ "d647cd71-6d93-4c62-9a7f-195af0d06fd4": [],
89
+ "f402001c-4a2e-41e8-9cab-1f11754cc2a9": [],
90
+ "2dda88d0-b646-4c89-9d6f-2a49e86f3d51": [],
91
+ "5b520d06-c3f1-495f-bcbd-278df820e187": [
92
+ [
93
+ "Hello",
94
+ "Hello! I'm ready to assist you. How can I help you today?"
95
+ ],
96
+ [
97
+ "What is 3 + 2?",
98
+ "3 + 2 = 5"
99
+ ],
100
+ [
101
+ "what is my name",
102
+ "I am an AI and do not have access to personal information. Therefore, I do not know your name. You would need to tell me your name."
103
+ ],
104
+ [
105
+ "ok my name is prince",
106
+ "Okay, Prince. It's nice to meet you! How can I help you today, Prince?"
107
+ ]
108
+ ],
109
+ "125dc06d-62aa-4f6b-aa75-9e59a0e15a86": [],
110
+ "f5f01d4b-5f75-4245-99d8-166ace23aa41": [
111
+ [
112
+ "Hi",
113
+ "Hi there! How can I help you today? I'm ready to answer your questions to the best of my ability, keeping in mind accuracy, safety, and respect. Let me know what's on your mind!"
114
+ ],
115
+ [
116
+ "My name is beta",
117
+ "It's nice to meet you, Beta! I'm here to assist you however I can. Just let me know what you need."
118
+ ]
119
+ ],
120
+ "7e3784cd-525c-4248-b4ab-0ed7dc4b02ad": []
121
+ }
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ langchain
2
+ python-dotenv
3
+ google-generativeai>=0.3.0
4
+ langchain-google-genai>=0.0.2
5
+ langgraph>=0.0.5
6
+ gradio>=3.0.0