Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
import os
|
2 |
import nltk
|
3 |
import requests
|
4 |
-
import datetime
|
5 |
|
6 |
# Use a directory within the user's home directory
|
7 |
nltk_data_dir = os.path.expanduser("~/.nltk_data")
|
@@ -10,7 +9,6 @@ nltk.data.path.append(nltk_data_dir)
|
|
10 |
|
11 |
# Download NLTK data
|
12 |
nltk.download('punkt', download_dir=nltk_data_dir, quiet=True)
|
13 |
-
|
14 |
import chainlit as cl
|
15 |
from llama_index.core import VectorStoreIndex, Document
|
16 |
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
@@ -23,40 +21,45 @@ import pandas as pd
|
|
23 |
|
24 |
load_dotenv()
|
25 |
|
26 |
-
#
|
27 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
28 |
FMP_API_KEY = os.getenv("FMP_API_KEY")
|
29 |
|
|
|
30 |
embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
31 |
llm = Groq(model="llama3-70b-8192", api_key=GROQ_API_KEY)
|
32 |
|
|
|
33 |
service_context = ServiceContext.from_defaults(
|
34 |
llm=llm,
|
35 |
embed_model=embed_model,
|
36 |
node_parser=SentenceSplitter(chunk_size=1000, chunk_overlap=200)
|
37 |
)
|
38 |
|
39 |
-
def
|
40 |
"""
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
43 |
"""
|
44 |
-
|
45 |
-
|
46 |
-
f"?symbol={symbol}&type=10-K&apikey={FMP_API_KEY}"
|
47 |
-
)
|
48 |
try:
|
49 |
-
response = requests.get(
|
50 |
response.raise_for_status()
|
51 |
-
|
52 |
-
|
53 |
-
if not
|
54 |
-
return f"No
|
55 |
-
|
56 |
-
#
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
60 |
|
61 |
except requests.exceptions.HTTPError as http_err:
|
62 |
return f"HTTP error occurred: {http_err}"
|
@@ -65,84 +68,78 @@ def fetch_annual_report_10k(symbol: str) -> str:
|
|
65 |
except Exception as err:
|
66 |
return f"An unexpected error occurred: {err}"
|
67 |
|
68 |
-
# Prompts
|
69 |
summary_prompt = (
|
70 |
-
"You are a world-class financial analyst with extensive experience analyzing
|
71 |
-
"
|
72 |
-
"Answer in extensive bullet points
|
73 |
)
|
74 |
|
75 |
question_prompt = (
|
76 |
-
"You are a financial analyst with extensive experience analyzing
|
77 |
-
"Read the
|
78 |
-
"Ask questions that provide strategic
|
79 |
-
"
|
|
|
|
|
80 |
)
|
81 |
|
82 |
@cl.on_chat_start
|
83 |
async def on_chat_start():
|
84 |
-
# Ask the user for a ticker symbol
|
85 |
ticker_response = await cl.AskUserMessage(
|
86 |
content=(
|
87 |
-
"This tool is designed to analyze
|
88 |
-
"Provide the company's ticker symbol, and the tool will fetch the latest
|
89 |
-
"It generates summaries and strategic due diligence. Ask your own questions afterwards
|
90 |
"Please enter the ticker symbol for the company you want to analyze (e.g. MSFT):"
|
91 |
)
|
92 |
).send()
|
93 |
|
94 |
-
if not ticker_response or 'content' not in ticker_response:
|
95 |
-
await cl.Message(content="No ticker symbol provided. Please enter a valid ticker symbol to proceed.").send()
|
96 |
-
return
|
97 |
-
|
98 |
ticker_symbol = ticker_response['content'].upper()
|
99 |
-
|
|
|
100 |
await msg.send()
|
101 |
|
102 |
try:
|
103 |
-
# Fetch the
|
104 |
-
|
105 |
-
|
106 |
-
# Check if
|
107 |
-
if
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
await cl.Message(content=
|
112 |
return
|
113 |
|
114 |
-
# Create a Document
|
115 |
-
document = Document(text=
|
116 |
|
117 |
-
#
|
118 |
-
index = VectorStoreIndex.from_documents(
|
|
|
|
|
119 |
|
120 |
-
#
|
121 |
cl.user_session.set("index", index)
|
122 |
|
123 |
-
#
|
124 |
query_engine = index.as_query_engine()
|
125 |
summary_response = await cl.make_async(query_engine.query)(summary_prompt)
|
126 |
-
|
127 |
-
await cl.Message(content=f"**Summary:**\n{summary_text}").send()
|
128 |
|
129 |
-
#
|
130 |
questions_response = await cl.make_async(query_engine.query)(question_prompt)
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
]
|
137 |
-
|
138 |
-
await cl.Message(content="Generated strategic questions and answers:").send()
|
139 |
for question in relevant_questions:
|
140 |
-
await cl.
|
141 |
-
|
142 |
-
answer_text = str(answer_obj)
|
143 |
-
await cl.Message(content=f"**Answer:**\n{answer_text}").send()
|
144 |
|
145 |
-
msg.content = "Processing done. You can now ask more questions about the
|
146 |
await msg.update()
|
147 |
|
148 |
except Exception as e:
|
@@ -151,22 +148,17 @@ async def on_chat_start():
|
|
151 |
@cl.on_message
|
152 |
async def main(message: cl.Message):
|
153 |
index = cl.user_session.get("index")
|
|
|
154 |
if index is None:
|
155 |
await cl.Message(content="Please provide a ticker symbol first before asking questions.").send()
|
156 |
return
|
157 |
|
158 |
query_engine = index.as_query_engine()
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
await response_message.stream_token(token=token)
|
168 |
-
|
169 |
-
await response_message.send()
|
170 |
-
|
171 |
-
except Exception as e:
|
172 |
-
await cl.Message(content=f"Error while processing your question: {str(e)}").send()
|
|
|
1 |
import os
|
2 |
import nltk
|
3 |
import requests
|
|
|
4 |
|
5 |
# Use a directory within the user's home directory
|
6 |
nltk_data_dir = os.path.expanduser("~/.nltk_data")
|
|
|
9 |
|
10 |
# Download NLTK data
|
11 |
nltk.download('punkt', download_dir=nltk_data_dir, quiet=True)
|
|
|
12 |
import chainlit as cl
|
13 |
from llama_index.core import VectorStoreIndex, Document
|
14 |
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
|
|
21 |
|
22 |
load_dotenv()
|
23 |
|
24 |
+
# Fetch the API keys from environment variables
|
25 |
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
|
26 |
FMP_API_KEY = os.getenv("FMP_API_KEY")
|
27 |
|
28 |
+
# Initialize models
|
29 |
embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
30 |
llm = Groq(model="llama3-70b-8192", api_key=GROQ_API_KEY)
|
31 |
|
32 |
+
# Create service context
|
33 |
service_context = ServiceContext.from_defaults(
|
34 |
llm=llm,
|
35 |
embed_model=embed_model,
|
36 |
node_parser=SentenceSplitter(chunk_size=1000, chunk_overlap=200)
|
37 |
)
|
38 |
|
39 |
+
def fetch_earnings_transcript(symbol: str) -> str:
|
40 |
"""
|
41 |
+
Fetch the latest transcript for a company's earnings call.
|
42 |
+
Args:
|
43 |
+
- symbol (str): The stock ticker symbol (e.g., 'AAPL').
|
44 |
+
Returns:
|
45 |
+
- str: The earnings call transcript or an error message.
|
46 |
"""
|
47 |
+
transcript_url = f"https://financialmodelingprep.com/api/v3/earning_call_transcript/{symbol}?apikey={FMP_API_KEY}"
|
48 |
+
|
|
|
|
|
49 |
try:
|
50 |
+
response = requests.get(transcript_url, timeout=10)
|
51 |
response.raise_for_status()
|
52 |
+
transcript_data = response.json()
|
53 |
+
|
54 |
+
if not transcript_data:
|
55 |
+
return f"No transcript available for {symbol}."
|
56 |
+
|
57 |
+
# Extract the first available transcript
|
58 |
+
latest_transcript = transcript_data[0].get("content", "")
|
59 |
+
if not latest_transcript:
|
60 |
+
return f"No transcript content found for {symbol}."
|
61 |
+
|
62 |
+
return latest_transcript
|
63 |
|
64 |
except requests.exceptions.HTTPError as http_err:
|
65 |
return f"HTTP error occurred: {http_err}"
|
|
|
68 |
except Exception as err:
|
69 |
return f"An unexpected error occurred: {err}"
|
70 |
|
71 |
+
# Prompts
|
72 |
summary_prompt = (
|
73 |
+
"You are a world-class financial analyst with extensive experience analyzing quarterly reports. "
|
74 |
+
"Give me a comprehensive summary of the earnings report. Focus on the Strategic Insights and Key Financial Figures. "
|
75 |
+
"Answer in extensive bullet points please."
|
76 |
)
|
77 |
|
78 |
question_prompt = (
|
79 |
+
"You are a financial analyst with extensive experience analyzing quarterly reports. "
|
80 |
+
"Read the earnings call transcript and earnings presentation report and generate 10 questions focusing on the strategic insights and financial figures. "
|
81 |
+
"Ask questions that require precise answers and provide strategic insight into the company's financial and strategic performance, such as revenue growth, market trends, profit margins, and more. "
|
82 |
+
"Only ask questions that can be answered using the provided document, without making any assumptions or inferences beyond the text. "
|
83 |
+
"Please format the questions as a list with a simple '1. Question 1', '2. Question 2', etc. structure. "
|
84 |
+
"Unless retrievable from the documents, don't ask questions which cannot be compared to previous periods."
|
85 |
)
|
86 |
|
87 |
@cl.on_chat_start
|
88 |
async def on_chat_start():
|
|
|
89 |
ticker_response = await cl.AskUserMessage(
|
90 |
content=(
|
91 |
+
"This tool is designed to analyze earnings call transcripts for publicly traded companies. "
|
92 |
+
"Provide the company's ticker symbol, and the tool will fetch the latest earnings call transcript. "
|
93 |
+
"It generates summaries and strategic due diligence. Ask your own questions afterwards. \n\n"
|
94 |
"Please enter the ticker symbol for the company you want to analyze (e.g. MSFT):"
|
95 |
)
|
96 |
).send()
|
97 |
|
|
|
|
|
|
|
|
|
98 |
ticker_symbol = ticker_response['content'].upper()
|
99 |
+
|
100 |
+
msg = cl.Message(content=f"Retrieving earnings call transcript for {ticker_symbol}...")
|
101 |
await msg.send()
|
102 |
|
103 |
try:
|
104 |
+
# Fetch the transcript using FMP API
|
105 |
+
transcript_text = fetch_earnings_transcript(ticker_symbol)
|
106 |
+
|
107 |
+
# Check if an error message was returned
|
108 |
+
if transcript_text.startswith("No transcript") or \
|
109 |
+
transcript_text.startswith("HTTP error") or \
|
110 |
+
transcript_text.startswith("Request error") or \
|
111 |
+
transcript_text.startswith("An unexpected error occurred"):
|
112 |
+
await cl.Message(content=transcript_text).send()
|
113 |
return
|
114 |
|
115 |
+
# Create a Document object with the transcript text
|
116 |
+
document = Document(text=transcript_text, metadata={"company": ticker_symbol})
|
117 |
|
118 |
+
# Create index
|
119 |
+
index = VectorStoreIndex.from_documents(
|
120 |
+
[document], service_context=service_context
|
121 |
+
)
|
122 |
|
123 |
+
# Store the index in the user session
|
124 |
cl.user_session.set("index", index)
|
125 |
|
126 |
+
# Generate summary
|
127 |
query_engine = index.as_query_engine()
|
128 |
summary_response = await cl.make_async(query_engine.query)(summary_prompt)
|
129 |
+
await cl.Message(content=f"**Summary:**\n{summary_response}").send()
|
|
|
130 |
|
131 |
+
# Generate questions
|
132 |
questions_response = await cl.make_async(query_engine.query)(question_prompt)
|
133 |
+
questions_format = str(questions_response).split('\n')
|
134 |
+
relevant_questions = [question.strip() for question in questions_format if question.strip() and question.strip()[0].isdigit()]
|
135 |
+
|
136 |
+
# Answer generated questions
|
137 |
+
await cl.Message(content="Generated questions and answers:").send()
|
|
|
|
|
|
|
138 |
for question in relevant_questions:
|
139 |
+
response = await cl.make_async(query_engine.query)(question)
|
140 |
+
await cl.Message(content=f"**{question}**\n{response}").send()
|
|
|
|
|
141 |
|
142 |
+
msg.content = "Processing done. You can now ask more questions about the earnings call transcript!"
|
143 |
await msg.update()
|
144 |
|
145 |
except Exception as e:
|
|
|
148 |
@cl.on_message
|
149 |
async def main(message: cl.Message):
|
150 |
index = cl.user_session.get("index")
|
151 |
+
|
152 |
if index is None:
|
153 |
await cl.Message(content="Please provide a ticker symbol first before asking questions.").send()
|
154 |
return
|
155 |
|
156 |
query_engine = index.as_query_engine()
|
157 |
+
|
158 |
+
response = await cl.make_async(query_engine.query)(message.content)
|
159 |
+
|
160 |
+
response_message = cl.Message(content="")
|
161 |
+
for token in str(response):
|
162 |
+
await response_message.stream_token(token=token)
|
163 |
+
|
164 |
+
await response_message.send()
|
|
|
|
|
|
|
|
|
|
|
|