-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchatbot_without_hitl.py
More file actions
122 lines (99 loc) · 3.19 KB
/
Copy pathchatbot_without_hitl.py
File metadata and controls
122 lines (99 loc) · 3.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# backend.py
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.tools import tool
from dotenv import load_dotenv
import requests
load_dotenv()
# -------------------
# 1. LLM
# -------------------
llm = ChatOpenAI()
# -------------------
# 2. Tools
# -------------------
@tool
def get_stock_price(symbol: str) -> dict:
"""
Fetch latest stock price for a given symbol (e.g. 'AAPL', 'TSLA')
using Alpha Vantage with API key in the URL.
"""
url = (
"https://www.alphavantage.co/query"
f"?function=GLOBAL_QUOTE&symbol={symbol}&apikey=C9PE94QUEW9VWGFM"
)
r = requests.get(url)
return r.json()
@tool
def purchase_stock(symbol: str, quantity: int) -> dict:
"""
Simulate purchasing a given quantity of a stock symbol.
NOTE: This is a mock implementation:
- No real brokerage API is called.
- It simply returns a confirmation payload.
"""
return {
"status": "success",
"message": f"Purchase order placed for {quantity} shares of {symbol}.",
"symbol": symbol,
"quantity": quantity,
}
tools = [get_stock_price, purchase_stock]
llm_with_tools = llm.bind_tools(tools)
# -------------------
# 3. State
# -------------------
class ChatState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
# -------------------
# 4. Nodes
# -------------------
def chat_node(state: ChatState):
"""LLM node that may answer or request a tool call."""
messages = state["messages"]
response = llm_with_tools.invoke(messages)
return {"messages": [response]}
tool_node = ToolNode(tools)
# -------------------
# 5. Checkpointer (in-memory)
# -------------------
memory = MemorySaver()
# -------------------
# 6. Graph
# -------------------
graph = StateGraph(ChatState)
graph.add_node("chat_node", chat_node)
graph.add_node("tools", tool_node)
graph.add_edge(START, "chat_node")
graph.add_conditional_edges("chat_node", tools_condition)
graph.add_edge("tools", "chat_node")
chatbot = graph.compile(checkpointer=memory)
# -------------------
# 7. Simple usage example (CLI)
# -------------------
if __name__ == "__main__":
print("📈 Stock Bot with Tools (get_stock_price, purchase_stock)")
print("Type 'exit' to quit.\n")
# thread_id still works with MemorySaver (conversation kept in RAM)
thread_id = "demo-thread"
while True:
user_input = input("You: ")
if user_input.lower().strip() in {"exit", "quit"}:
print("Goodbye!")
break
# Build initial state for this turn
state = {"messages": [HumanMessage(content=user_input)]}
# Run the graph
result = chatbot.invoke(
state,
config={"configurable": {"thread_id": thread_id}},
)
# Get the latest message from the assistant
messages = result["messages"]
last_msg = messages[-1]
print(f"Bot: {last_msg.content}\n")