devchavda11 commited on
Commit
e0b3935
·
verified ·
1 Parent(s): cc5d89b

Upload 2 files

Browse files
Files changed (2) hide show
  1. chat_langraph.py +187 -0
  2. frontend_streamlit.py +129 -0
chat_langraph.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_google_genai import ChatGoogleGenerativeAI
2
+ from langchain_ollama import ChatOllama
3
+ from langchain_core.messages import BaseMessage,ChatMessage,ToolMessage,AIMessage,SystemMessage,HumanMessage,messages_from_dict
4
+ from langgraph.graph import StateGraph
5
+ from langgraph.graph import add_messages , START , END
6
+ from langgraph.checkpoint.base import CheckpointTuple
7
+ from typing import TypedDict,Annotated , List
8
+ from langchain_core.tools import tool
9
+ from langgraph.prebuilt.tool_node import ToolNode
10
+ from langgraph.checkpoint.sqlite import SqliteSaver
11
+ from langchain_core.prompts import ChatPromptTemplate
12
+ import sqlite3
13
+ import subprocess
14
+ import requests
15
+ from chat_image_analyser import image_analyser
16
+ from erpfunctionalities import *
17
+ from htmltomarkdown import *
18
+ from datetime import datetime , date
19
+ class chatstate(TypedDict):
20
+ messages : Annotated[List[BaseMessage], add_messages]
21
+
22
+ user_id = "23IT441"
23
+ password = "09122005"
24
+ api = "AIzaSyA5zvErF4vUmAoslVzkOBUfvSCSoW0vjEA"
25
+ LANGSEARCH_API_KEY = "sk-f1a8f996f9e44b43adf9943e43e8582b"
26
+
27
+ # llm = ChatOllama(model = "qwen3:8b" , temperature = 0.5)
28
+ llm = ChatGoogleGenerativeAI(model = "gemini-2.5-flash" , temperature = 0.2 , api_key = api)
29
+ system = SystemMessage(
30
+ content=
31
+ f"""
32
+ --> Todays date is : {datetime.today()} + " \n" + Dayno : {datetime.today().date().weekday()}
33
+ You are an assistant that is practical, tool-aware, and outcome-oriented. Aim for correctness and clarity; avoid hallucinations.
34
+ Rules (short & general):
35
+
36
+ 1. Prefer text answers and code when the user only asks for examples or explanations.
37
+ 2. If the user explicitly asks you to *create, save, run, or modify* a file or perform a system action (e.g., "create sales.xlsx", "save as report.py", "run this script now"), **call the appropriate tool(s) generally go with python tool to achieve the tasks which are not available directly.** to make that happen. Do not return code only in those cases.
38
+ 3. If you produce code via a tool, and the user has asked for the file/action to be created or executed, write the file and execute it (write_file + run_cmd_command or the relevant tools) unless the user explicitly says "only give code".
39
+ 4. Do not run or write obviously destructive commands (delete, format, rm -rf, wipe, format C:, etc.) without explicit, separate confirmation from the user. For any command that could be destructive, always ask one short confirmation (yes/no).
40
+ 5. Keep tool inputs minimal and focused. When calling a tool, include only the parameters needed. After the tool returns, always validate the result and summarize it for the user (one-line result + any errors).
41
+ 6. If a requested action fails (permission, path, syntax error), explain the failure, propose a fix, and offer to retry when the user approves.
42
+ 7. Do not make assumptions — ask the user if in doubt. If you need clarification, ask a single short question; otherwise make a best-effort decision to proceed.
43
+ 8. If you are unsure whether to call a tool, prefer asking a *single* clarifying question; otherwise make a best-effort decision to proceed.
44
+ 9. Do not make assumptions about the system of the user , u have command prompt access it to gather the information about the system of the user..
45
+ 10. Respond in the same language as the user.
46
+
47
+ Tone: concise, helpful, and decisive.
48
+
49
+ """
50
+ )
51
+
52
+ conn = sqlite3.connect("chatbot.db" , check_same_thread=False)
53
+ checkpointer = SqliteSaver(conn=conn)
54
+ @tool
55
+ def add(a : int , b:int):
56
+ """Adds two numbers """
57
+ return a+b
58
+ @tool
59
+ def reverse(string : str):
60
+ """Reverses a string..."""
61
+ return string[::-1]
62
+ @tool
63
+ def evaluate(string : str):
64
+ """Evaluates a simple mathematical expression but it should be passed as a string..."""
65
+ print(eval(string))
66
+ return eval(string)
67
+ @tool
68
+ def write_file(name : str , extension : str , content : str):
69
+ """Writes a file given name extension and content of the file ,
70
+ it only supports txt files and coding files currently , Use the python script to create other file types."""
71
+ with open(f"{name}.{extension}" , "w" , encoding='utf-8') as f:
72
+ f.write(content)
73
+ return f"Content : {content} is writtened and saved to {name}.{extension} ."
74
+
75
+
76
+ @tool
77
+ def run_python_file(filename:str):
78
+ """Runs the python file when given the filename of it."""
79
+ if(filename[-1:-3:-1] != "py"):
80
+ filename += ".py"
81
+ msg = run_cmd_command(f"python {filename}")
82
+ return msg
83
+ @tool
84
+ def run_cmd_command(command: str) -> str:
85
+
86
+ """Any windows command is allowed"""
87
+ try:
88
+ # Execute the command and capture the output
89
+ result = subprocess.run(command, shell=True, check=True, text=True, capture_output=True)
90
+ print(result)
91
+ return result
92
+ except subprocess.CalledProcessError as e:
93
+ return f"An error occurred while executing the command: {e} "
94
+ @tool
95
+ def search_tool(query:str):
96
+ """Searches the query on web and gives brief description (in json) and url names...."""
97
+ response = requests.post("https://api.langsearch.com/v1/web-search",
98
+ headers={
99
+ "Authorization": f"Bearer {LANGSEARCH_API_KEY}",
100
+ "Content-Type":"application/json"
101
+ },
102
+ json={
103
+ "query":query,
104
+ "num_results":2
105
+ })
106
+
107
+ return response.json()
108
+
109
+ @tool
110
+ def image_analysis(query : str)->str:
111
+ """Analyzes an image given a query and generates a description based on conversational context.
112
+ It only analyses the file present in the current directory named as image.png , so if user asks for specific file rename the file and save to curr dir , otherwise just use this tool directly if not said anything...
113
+ Args: query : Any query regarding the image...
114
+ You dont have to worry about image name and anything it will be handled automatically. Finally just explain the returned text nicely .
115
+ Returns: str: A textual description of the image. Please give the final ans in detail and in your own words..."""
116
+ return image_analyser(query)
117
+
118
+
119
+ @tool
120
+ def get_fees_tool() -> str:
121
+ """Gives the fees detail of the student who is chatting..."""
122
+ return get_fees_details(user_id, password)
123
+ @tool
124
+ def get_attendance_tool() -> str:
125
+ """Gives the attendance details of the student who is chatting."""
126
+ return attendance(user_id, password)
127
+ @tool
128
+ def get_timetable_tool(target: str) -> str:
129
+ """Gives the timetable details of the student who is chatting when given a date in dd-mm-yy format
130
+ Timetable contains the holidays information as well."""
131
+ return timetable(user_id, password, target)
132
+ @tool
133
+ def get_student_details_tool() -> str:
134
+ """Gives the information about the student who is chatting."""
135
+ return student_details(user_id, password)
136
+ @tool
137
+ def get_result_tool(semester: int) -> str:
138
+ """Gets the result of the student who is chatting."""
139
+ return get_result_details(user_id, password, semester)
140
+ def shouldcontinue(state:chatstate):
141
+ if(state['messages'][-1].content == 'end'):
142
+ return 'end'
143
+ else:
144
+ return 'llmresponse'
145
+ def input_node(state:chatstate):
146
+ return {"messages":state['messages']}
147
+ def llmresponse(state:chatstate):
148
+ response = llm.invoke(state['messages'])
149
+ print(response.content)
150
+ return {'messages':[response]}
151
+ def checktool(state: chatstate):
152
+ last_msg = state['messages'][-1]
153
+ if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
154
+ fc = last_msg.additional_kwargs.get('function_call')
155
+ if fc and 'name' in fc:
156
+ print("Calling the tool", fc['name'])
157
+ elif last_msg.tool_calls[0]['name'] :
158
+ print("Calling the tool " ,last_msg.tool_calls[0]['name'] )
159
+ return "tool_node"
160
+ return "end"
161
+
162
+ tools = [add,reverse,evaluate,run_cmd_command,search_tool,write_file,image_analysis]
163
+ tool_node = ToolNode(tools=tools)
164
+ llm = llm.bind_tools(tools)
165
+ graph = StateGraph(chatstate)
166
+ graph.add_node('input_node' , input_node)
167
+ graph.add_node('llmresponse',llmresponse)
168
+ graph.add_node('tool_node',tool_node)
169
+ graph.add_edge(START , "input_node")
170
+ graph.add_edge("input_node" , "llmresponse")
171
+ graph.add_conditional_edges(
172
+ "llmresponse",
173
+ checktool,
174
+ {
175
+ "tool_node" : "tool_node",
176
+ "end":END
177
+ }
178
+ )
179
+ graph.add_edge("tool_node", "llmresponse")
180
+
181
+ workflow = graph.compile(checkpointer=checkpointer)
182
+ res = checkpointer.list(None)
183
+ def get_all_chat_ids():
184
+ s = set()
185
+ for chkpoint in res:
186
+ s.add(chkpoint.config.get('configurable').get('thread_id'))
187
+ return list(s)
frontend_streamlit.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from chat_langraph import system, workflow, HumanMessage, AIMessage, get_all_chat_ids, ToolMessage
3
+ import uuid
4
+ from chat_title_giver import give_me_the_title
5
+ import os
6
+ import base64
7
+ import re
8
+
9
+ st.title("College chatbot")
10
+ st.set_page_config(layout='wide')
11
+
12
+ def set_title(messages):
13
+ if messages:
14
+ title = give_me_the_title(messages)
15
+ st.session_state.chat_dict[st.session_state.current_chat_id] = title
16
+
17
+ def set_config():
18
+ return {'configurable': {'thread_id': st.session_state.current_chat_id}}
19
+
20
+
21
+ def load_session_state():
22
+ if "chats" not in st.session_state:
23
+ st.session_state.chats = get_all_chat_ids()
24
+ if "current_chat_id" not in st.session_state:
25
+ if len(st.session_state.chats) > 0:
26
+ st.session_state.current_chat_id = st.session_state.chats[-1]
27
+ else:
28
+
29
+ new_id = str(uuid.uuid4())
30
+ st.session_state.chats.append(new_id)
31
+ st.session_state.current_chat_id = new_id
32
+ if "chat_dict" not in st.session_state:
33
+ st.session_state.chat_dict = {}
34
+ if "uploaded_image" not in st.session_state:
35
+ st.session_state.uploaded_image = None
36
+
37
+ def render_sidebar():
38
+ with st.sidebar:
39
+ st.title("Chats")
40
+ if st.button("➕ New Chat"):
41
+ new_id = str(uuid.uuid4())
42
+ st.session_state.chats.append(new_id)
43
+ st.session_state.current_chat_id = new_id
44
+ config = {'configurable': {'thread_id': new_id}}
45
+ workflow.update_state(config, {"messages": [system]})
46
+ st.session_state.chat_dict[new_id] = "New Chat"
47
+ st.rerun()
48
+ for chat_id in st.session_state.chats:
49
+ if st.button(st.session_state.chat_dict.get(chat_id, "New Chat"), key=chat_id):
50
+ st.session_state.current_chat_id = chat_id
51
+
52
+ def loadchats():
53
+ if "current_chat_id" not in st.session_state:
54
+ return []
55
+ config = {'configurable': {'thread_id': st.session_state.current_chat_id}}
56
+ state = workflow.get_state(config)
57
+ messages = state.values.get("messages", [])
58
+ for message in messages:
59
+ if isinstance(message, HumanMessage):
60
+ if message.content and message.content.strip():
61
+ with st.chat_message("human"):
62
+ st.write(message.content)
63
+ elif isinstance(message, AIMessage):
64
+ content = st.expander("Thinking...", expanded=False)
65
+ if message.content and message.content.strip():
66
+ text = message.content
67
+ m = re.search(r'</\s*think\s*>', text, re.IGNORECASE)
68
+ if m:
69
+ thinking_text = text[:m.start()].strip()
70
+ rest = text[m.end():].strip()
71
+ if thinking_text:
72
+ with content:
73
+ st.write(thinking_text)
74
+ if rest:
75
+ with st.chat_message("assistant"):
76
+ st.write(rest)
77
+ else:
78
+ with st.chat_message("assistant"):
79
+ st.write(text)
80
+ elif isinstance(message, ToolMessage):
81
+ with st.chat_message("assistant"):
82
+ st.info(f"Used a tool")
83
+ return messages
84
+
85
+ load_session_state()
86
+ render_sidebar()
87
+ if("current_chat_id" in st.session_state):
88
+ loadchats()
89
+ user_input = st.chat_input("Your message: ")
90
+ if(user_input is not None):
91
+ response_placeholder = st.empty()
92
+ with st.chat_message("human"):
93
+ st.write(user_input)
94
+
95
+ full_response = ""
96
+ answer_response = ""
97
+ is_thinking = True
98
+ with st.chat_message("assistant"):
99
+ content = st.expander("Thinking...", expanded=True)
100
+ resp = st.empty()
101
+ resp2 = None
102
+ with content:
103
+ resp2 = st.empty()
104
+ for messages,metadata in workflow.stream({"messages":[system,HumanMessage(user_input)]} , config={'configurable': {'thread_id': st.session_state.current_chat_id}} , stream_mode="messages"):
105
+ if(isinstance(messages , AIMessage) and messages.content.strip()):
106
+ if(messages.content.startswith("<think>")):
107
+ is_thinking = True
108
+ resp2.markdown(full_response + "|")
109
+ if is_thinking:
110
+ full_response += messages.content
111
+ with content:
112
+ resp2.markdown(full_response + "|")
113
+ if "</think>" in full_response:
114
+ is_thinking = False
115
+ full_response = full_response.replace("</think>", "")
116
+ resp2.markdown(full_response)
117
+ else:
118
+ answer_response += messages.content
119
+ resp.markdown(answer_response + "|")
120
+ if(isinstance(messages ,ToolMessage) and messages.content.strip()):
121
+ st.info(f"Using appropriate tool")
122
+
123
+ st.rerun()
124
+
125
+
126
+
127
+
128
+
129
+