-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathllm.py
194 lines (164 loc) · 7.03 KB
/
llm.py
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import os
from dotenv import load_dotenv
from langchain_openai import AzureChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph, END
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
import requests, finnhub, datetime, timedelta, json
from langgraph.prebuilt import ToolNode, tools_condition
import pandas as pd
# Load environment variables from .env file
load_dotenv()
# Define the system prompt for finance GPT
SYSTEM_PROMPT = """
You are Finance GPT, an AI-powered financial assistant designed to help users manage personal finances.
Your role is to provide insights on budgeting, savings, investments, debt management, and financial literacy.
# Tone & Personality:
- Friendly, professional, and approachable.
- Clear and concise, avoiding unnecessary jargon.
- Encouraging but realistic—no guaranteed financial predictions.
# Capabilities:
- Explain budgeting techniques (e.g., 50/30/20 rule).
- Provide debt management strategies (e.g., snowball vs. avalanche method).
- Offer general investment insights without giving direct financial advice.
- Guide users on savings plans and emergency funds.
- Analyze and categorize transactions if user data is provided.
- Educate users on personal finance concepts and common pitfalls.
# Limitations:
- You are NOT a licensed financial advisor—always encourage users to seek professional advice.
- Avoid speculative predictions or tax/legal compliance guidance.
- Do not store or process sensitive financial data beyond a session.
# User Engagement Rules:
- Ask clarifying questions before giving financial guidance.
- Adapt responses to user expertise level (beginner, intermediate, advanced).
- Provide actionable steps with examples.
- Encourage healthy financial habits (e.g., building emergency funds, reducing bad debt).
# RESPONSE FORMATTING:
- Always return response in proper Markdown Syntax.
- Use bullet points for lists and headings for sections.
- Use Bold, Italics, and Hyperlinks for emphasis and references.
"""
# Creating a Finnhub client to access stock data
finnhub_client = finnhub.Client(os.getenv("FINNHUB_API_KEY"))
# Creating a Weather Tool
# @tool
# def getWeather(city: str):
# """Call the weather API to get the weather of the city""""
# url = f"http://api.weatherapi.com/v1/current.json?key=6f1d93acbcc0475eb09163014252901&q={city}&aqi=no"
# try:
# response = requests.get(url)
# response.raise_for_status() # Raise an error for bad responses (4xx, 5xx)
# return response.json()
# except requests.exceptions.RequestException as e:
# print(f"Error fetching weather data: {e}")
# return None
# Creating a Company Profile Tool
@tool
def getStockData(symbol: str):
"""Call API to Get general information of a company. You can query by symbol, ISIN or CUSIP."""
try:
response = finnhub_client.company_profile2(symbol=symbol)
return response
except requests.exceptions.RequestException as e:
print(f"Error fetching company profile data: {e}")
return None
# Creating a Stock Recommendation Tool
@tool
def getStockRecommendation(symbol: str):
"""Call the stock API to Get latest analyst recommendation trends for a company"""
try:
response = finnhub_client.recommendation_trends(symbol=symbol)
print(response)
return response
except requests.exceptions.RequestException as e:
print(f"Error fetching company recommendation data: {e}")
return None
# Creating a Stock Pice Tool
@tool
def getStockPrice(symbol: str):
"""Call the stock API to get the stock price of the symbol for requested company"""
try:
response = finnhub_client.quote(symbol=symbol)
return response
except requests.exceptions.RequestException as e:
print(f"Error fetching company quote data: {e}")
return None
# Creating a Company Earnings History Tool
@tool
def getCompanyEarnings(symbol: str):
"""Call the stock API to get the earnings history of the symbol for requested company"""
try:
response = finnhub_client.company_earnings(symbol=symbol)
return response
except requests.exceptions.RequestException as e:
print(f"Error fetching company earnings data: {e}")
return None
# Creating a Company News Tools
@tool
def getCompanyNews(symbol: str):
"""Fetch company news from the API and summarize it."""
try:
# Get current date and 7 days ago
to_date = datetime.date.today()
from_date = to_date - datetime.timedelta(days=7)
# Fetch news data
response = finnhub_client.company_news(symbol=symbol, _from=str(from_date), to=str(to_date))
if not response:
return json.dumps({"error": "No news data available"}, indent=4)
# Extract summaries, handling missing keys
summaries = []
for item in response:
if "summary" in item and item["summary"]:
summaries.append(item["summary"])
if not summaries:
return json.dumps({"error": "No summaries found in response"}, indent=4)
# Combine summaries
combined_summaries = "\n".join(summaries)
# Convert to JSON object
result_json = json.dumps({"summaries": combined_summaries}, indent=4, ensure_ascii=False)
return result_json
except finnhub.FinnhubAPIException as e:
print(f"API error: {e}")
return json.dumps({"error": f"API error: {e}"}, indent=4)
except Exception as e:
print(f"Unexpected error: {e}")
return json.dumps({"error": f"Unexpected error: {e}"}, indent=4)
# Initialize Azure OpenAI LLM
tools = [getStockData, getStockRecommendation, getCompanyNews, getStockPrice, getCompanyEarnings]
llm = AzureChatOpenAI(
deployment_name=os.getenv("OPENAI_API_DEPLOYMENT"),
model=os.getenv("OPENAI_API_MODEL"),
temperature=0.8,
max_tokens=1000
).bind_tools(tools)
class CustomState(TypedDict):
messages: Annotated[list, add_messages]
language: str
# Define a new graph
workflow = StateGraph(CustomState)
# Action taken by the home node
def invoke_llm(state: CustomState):
prompt_template = ChatPromptTemplate([
("system", SYSTEM_PROMPT),
MessagesPlaceholder("messages")
])
prompt = prompt_template.invoke(state)
response = llm.invoke(prompt)
return {"messages": response}
# response = llm.invoke(state["messages"])
# return {"messages": response}
# Define a graph with a single node
workflow.add_edge(START, "home")
workflow.add_node("home", invoke_llm)
tool_node = ToolNode(tools)
workflow.add_node("tools", tool_node)
workflow.add_conditional_edges("home", tools_condition, ["tools", END])
workflow.add_edge("tools", "home")
# Compile the workflow, specifying a checkpointer
# that persists checkpoints to memory
memory = MemorySaver()
graph = workflow.compile(checkpointer=memory)