sidmanale643 commited on
Commit
17bd849
Β·
verified Β·
1 Parent(s): 64ac7c7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +453 -34
app.py CHANGED
@@ -1,34 +1,453 @@
1
- import streamlit as st
2
- import requests
3
-
4
- st.title("Company Sentiment Analyzer")
5
-
6
- company_name = st.text_input("Enter Company Name", "Tesla")
7
- model_provider = st.selectbox("Model Provider", options=["Ollama", "Groq"])
8
-
9
- if st.button("Fetch Sentiment Data"):
10
- api_url = (
11
- f"http://localhost:8000/home?"
12
- f"company_name={company_name}&model_provider={model_provider}"
13
- )
14
-
15
- try:
16
- response = requests.get(api_url)
17
- response.raise_for_status()
18
-
19
- data = response.json()
20
-
21
- st.subheader("Company Name")
22
- st.write(data.get("company_name"))
23
-
24
- st.subheader("Final Report")
25
- st.write(data.get("final_report"))
26
-
27
- st.subheader("πŸ”Š Audio Output")
28
- audio_file = "output.mp3"
29
- if audio_file:
30
- st.audio(audio_file)
31
-
32
- except requests.exceptions.RequestException as e:
33
- st.error(f"Error fetching data: {e}")
34
- #
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import os
4
+ from typing import Literal, List
5
+ from tavily import TavilyClient
6
+ from pydantic import BaseModel
7
+ from ollama import chat
8
+ from dotenv import load_dotenv
9
+ from groq import Groq
10
+ import instructor
11
+
12
+
13
+ GROQ_API_KEY = "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"
14
+ ELEVEN_LABS_API_KEY = "sk_a927222500aab9665f83f078b92e833e7ec1389ee68238c0"
15
+ TAVILY_API_KEY = "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"
16
+
17
+ load_dotenv()
18
+
19
+
20
+ def fetch_from_web(query):
21
+ tavily_client = TavilyClient(api_key=TAVILY_API_KEY)
22
+ response = tavily_client.search(
23
+ query,
24
+ include_raw_content=True,
25
+ max_results=10,
26
+ topic="news",
27
+ search_depth="basic"
28
+ )
29
+ return {"sources": response['results']}
30
+
31
+
32
+ class Sentiment(BaseModel):
33
+ summary: str
34
+ reasoning: str
35
+ topics: List[str]
36
+ sentiment: Literal['positive', 'negative', 'neutral']
37
+
38
+
39
+ def analyze_sentiment(article, model_provider):
40
+ sentiment_prompt = f"""
41
+ Analyze the following news article about a company:
42
+
43
+ 1. **Summary**: Provide a comprehensive summary of the article's key points.
44
+
45
+ 2. **Sentiment Analysis**:
46
+ - Classify the overall sentiment toward the company as: POSITIVE, NEGATIVE, or NEUTRAL
47
+ - Support your classification with specific quotes, tone analysis, and factual evidence from the article
48
+ - Explain your reasoning for this sentiment classification in 2 to 3 lines.
49
+
50
+ 3. **Key Topics**:
51
+ - Identify 3-5 main topics discussed in the article
52
+ - Only give the name of the topics
53
+
54
+ Be as detailed and objective as possible in your reasoning.
55
+
56
+ Article Title: {article['title']}
57
+
58
+ Article: {article['raw_content']}
59
+ """
60
+
61
+ try:
62
+ if model_provider == "Ollama":
63
+ response = chat(
64
+ messages=[
65
+ {
66
+ 'role': 'user',
67
+ 'content': sentiment_prompt
68
+ }
69
+ ],
70
+ model='llama3.2:3b',
71
+ format=Sentiment.model_json_schema(),
72
+ )
73
+
74
+ sentiment_output = Sentiment.model_validate_json(response.message.content)
75
+
76
+ final_dict = {
77
+ "title": article["title"],
78
+ "summary": sentiment_output.summary,
79
+ "reasoning": sentiment_output.reasoning,
80
+ "topics": sentiment_output.topics,
81
+ "sentiment": sentiment_output.sentiment
82
+ }
83
+ else:
84
+ llm = Groq(api_key=GROQ_API_KEY)
85
+ llm = instructor.from_groq(llm, mode=instructor.Mode.TOOLS)
86
+
87
+ resp = llm.chat.completions.create(
88
+ model="llama-3.3-70b-versatile",
89
+ messages=[
90
+ {
91
+ "role": "user",
92
+ "content": sentiment_prompt,
93
+ }
94
+ ],
95
+ response_model=Sentiment,
96
+ )
97
+
98
+ sentiment_output = resp.model_dump()
99
+
100
+ final_dict = {
101
+ "title": article["title"],
102
+ "summary": sentiment_output.get("summary"),
103
+ "reasoning": sentiment_output.get("reasoning"),
104
+ "topics": sentiment_output.get("topics"),
105
+ "sentiment": sentiment_output.get("sentiment")
106
+ }
107
+
108
+ return final_dict
109
+
110
+ except Exception as e:
111
+ print(f"Error parsing sentiment output: {e}")
112
+ return None
113
+
114
+
115
+ def generate_comparative_sentiment(articles):
116
+ sentiment_counts = {"Positive": 0, "Negative": 0, "Neutral": 0}
117
+
118
+ for article in articles:
119
+ sentiment = article.get("sentiment", "").lower()
120
+ if sentiment == "positive":
121
+ sentiment_counts["Positive"] += 1
122
+ elif sentiment == "negative":
123
+ sentiment_counts["Negative"] += 1
124
+ elif sentiment == "neutral":
125
+ sentiment_counts["Neutral"] += 1
126
+
127
+ all_topics = []
128
+ for article in articles:
129
+ all_topics.extend(article.get("topics", []))
130
+
131
+ unique_topics = set(all_topics)
132
+
133
+ topic_counts = {}
134
+
135
+ for topic in unique_topics:
136
+ count = all_topics.count(topic)
137
+ topic_counts[topic] = count
138
+
139
+ common_topics = [topic for topic, count in topic_counts.items() if count > 1]
140
+ unique_topics = {}
141
+
142
+ for i, article in enumerate(articles):
143
+ article_topics = set(article.get("topics", []))
144
+ for j, other_article in enumerate(articles):
145
+ if i != j:
146
+ other_topics = set(other_article.get("topics", []))
147
+ unique_topics[f"Unique Topics in Article {i+1}"] = list(article_topics - other_topics)
148
+
149
+ comparative_sentiment = {
150
+ "Sentiment Distribution": sentiment_counts,
151
+ "Coverage Differences": "coverage_differences",
152
+ "Topic Overlap": {
153
+ "Common Topics": common_topics,
154
+ "Unique Topics in Article 1": unique_topics.get("Unique Topics in Article 1", []),
155
+ "Unique Topics in Article 2": unique_topics.get("Unique Topics in Article 2", []),
156
+ "Unique Topics in Article 3": unique_topics.get("Unique Topics in Article 3", []),
157
+ "Unique Topics in Article 4": unique_topics.get("Unique Topics in Article 4", []),
158
+ "Unique Topics in Article 5": unique_topics.get("Unique Topics in Article 5", []),
159
+ "Unique Topics in Article 6": unique_topics.get("Unique Topics in Article 6", []),
160
+ "Unique Topics in Article 7": unique_topics.get("Unique Topics in Article 7", []),
161
+ "Unique Topics in Article 8": unique_topics.get("Unique Topics in Article 8", []),
162
+ "Unique Topics in Article 9": unique_topics.get("Unique Topics in Article 9", []),
163
+ "Unique Topics in Article 10": unique_topics.get("Unique Topics in Article 10", [])
164
+ },
165
+ }
166
+
167
+ return comparative_sentiment
168
+
169
+
170
+ def get_summaries_by_sentiment(articles):
171
+ pos_sum = []
172
+ neg_sum = []
173
+ neutral_sum = []
174
+
175
+ for article in articles:
176
+ sentiment = article.get("sentiment", "").lower()
177
+ title = article.get("title", "No Title")
178
+ summary = article.get("summary", "No Summary")
179
+
180
+ article_text = f'Title: {title}\nSummary: {summary}'
181
+
182
+ if sentiment == "positive":
183
+ pos_sum.append(article_text)
184
+ elif sentiment == "negative":
185
+ neg_sum.append(article_text)
186
+ elif sentiment == "neutral":
187
+ neutral_sum.append(article_text)
188
+
189
+ pos_sum = "\n\n".join(pos_sum) if pos_sum else "No positive articles available."
190
+ neg_sum = "\n\n".join(neg_sum) if neg_sum else "No negative articles available."
191
+ neutral_sum = "\n\n".join(neutral_sum) if neutral_sum else "No neutral articles available."
192
+
193
+ return pos_sum, neg_sum, neutral_sum
194
+
195
+
196
+ def comparative_analysis(pos_sum, neg_sum, neutral_sum, model_provider):
197
+ prompt = f"""
198
+ Perform a detailed comparative analysis of the sentiment across three categories of articles (Positive, Negative, and Neutral) about a specific company. Address the following aspects:
199
+
200
+ 1. **Sentiment Breakdown**: Identify how each category (positive, negative, and neutral) portrays the company. Highlight the language, tone, and emotional cues that shape the sentiment.
201
+
202
+ 2. **Key Themes and Topics**: Compare the primary themes and narratives within each sentiment group. What aspects of the company's operations, performance, or reputation does each category focus on?
203
+
204
+ 3. **Perceived Company Image**: Analyze how each sentiment type influences public perception of the company. What impression is created by positive vs. negative vs. neutral coverage?
205
+
206
+ 4. **Bias and Framing**: Evaluate whether any of the articles reflect explicit biases or specific agendas regarding the company. Are there patterns in how the company is framed across different sentiments?
207
+
208
+ 5. **Market or Stakeholder Impact**: Discuss potential effects on stakeholders (e.g., investors, customers, regulators) based on the sentiment of each article type.
209
+
210
+ 6. **Comparative Insights**: Provide a concise summary of the major differences and commonalities between the three sentiment groups. What overall narrative emerges about the company?
211
+
212
+ ### Positive Articles:
213
+ {pos_sum}
214
+
215
+ ### Negative Articles:
216
+ {neg_sum}
217
+
218
+ ### Neutral Articles:
219
+ {neutral_sum}
220
+ """
221
+
222
+ if model_provider == "Ollama":
223
+ response = chat(
224
+ messages=[
225
+ {
226
+ 'role': 'user',
227
+ 'content': prompt
228
+ }
229
+ ],
230
+ model='llama3.2:3b'
231
+ )
232
+ response = response.message.content
233
+
234
+ else:
235
+ llm = Groq(api_key=GROQ_API_KEY)
236
+
237
+ chat_completion = llm.chat.completions.create(
238
+ messages=[
239
+ {
240
+ "role": "user",
241
+ "content": prompt[:5000],
242
+ }
243
+ ],
244
+ model="llama-3.3-70b-versatile",
245
+ )
246
+ response = chat_completion.choices[0].message.content
247
+
248
+ return response
249
+
250
+
251
+ def generate_final_report(pos_sum, neg_sum, neutral_sum, comparative_sentiment, model_provider):
252
+ final_report_prompt = f"""
253
+ Corporate News Sentiment Analysis Report:
254
+
255
+ ### 1. Executive Summary
256
+ - Overview of sentiment distribution: {comparative_sentiment["Sentiment Distribution"]['Positive']} positive, {comparative_sentiment["Sentiment Distribution"]['Negative']} negative, {comparative_sentiment["Sentiment Distribution"]['Neutral']} neutral.
257
+ - Highlight the dominant narrative shaping the company's perception.
258
+ - Summarize key drivers behind positive and negative sentiments.
259
+
260
+ ### 2. Media Coverage Analysis
261
+ - Identify major news sources covering the company.
262
+ - Highlight patterns in coverage across platforms (e.g., frequency, timing).
263
+ - Identify whether media sentiment shifts over time.
264
+
265
+ ### 3. Sentiment Breakdown
266
+ - **Positive Sentiment:**
267
+ * Titles and sources: {pos_sum}
268
+ * Key themes, notable quotes, and focal areas (e.g., product, leadership).
269
+ - **Negative Sentiment:**
270
+ * Titles and sources: {neg_sum}
271
+ * Key themes, notable quotes, and areas of concern.
272
+ - **Neutral Sentiment:**
273
+ * Titles and sources: {neutral_sum}
274
+ * Key themes and neutral narratives.
275
+
276
+ ### 4. Narrative Analysis
277
+ - Identify primary storylines about the company.
278
+ - Analyze how the company is positioned (positive, neutral, negative).
279
+ - Detect shifts or emerging narratives over time.
280
+
281
+ ### 5. Key Drivers of Sentiment
282
+ - Identify specific events, announcements, or actions driving media sentiment.
283
+ - Evaluate sentiment linked to industry trends vs. company-specific factors.
284
+ - Highlight company strengths and weaknesses based on media portrayal.
285
+
286
+ ### 6. Competitive Context
287
+ - Identify competitor comparisons.
288
+ - Analyze how media sentiment about the company compares to industry standards.
289
+ - Highlight competitive advantages or concerns raised by the media.
290
+
291
+ ### 7. Stakeholder Perspective
292
+ - Identify how key stakeholders (e.g., investors, customers, regulators) are represented.
293
+ - Analyze stakeholder concerns and reputation risks/opportunities.
294
+
295
+ ### 8. Recommendations
296
+ - Suggest strategies to mitigate negative sentiment.
297
+ - Recommend approaches to amplify positive narratives.
298
+ - Provide messaging suggestions for future announcements.
299
+
300
+ ### 9. Appendix
301
+ - Full article details (title, publication, date, author, URL).
302
+ - Sentiment scoring methodology.
303
+ - Media monitoring metrics (reach, engagement, etc.).
304
+ """
305
+
306
+ if model_provider == "Ollama":
307
+ final_report = chat(
308
+ messages=[
309
+ {
310
+ 'role': 'user',
311
+ 'content': final_report_prompt
312
+ }
313
+ ],
314
+ model='llama3.2:3b'
315
+ )
316
+ response = final_report.message.content
317
+
318
+ else:
319
+ llm = Groq(api_key=GROQ_API_KEY)
320
+
321
+ chat_completion = llm.chat.completions.create(
322
+ messages=[
323
+ {
324
+ "role": "user",
325
+ "content": final_report_prompt[:5000],
326
+ }
327
+ ],
328
+ model="llama-3.3-70b-versatile",
329
+ )
330
+ response = chat_completion.choices[0].message.content
331
+
332
+ return response
333
+
334
+
335
+ def translate(report, model_provider):
336
+ translation_prompt = f"""
337
+ Translate the following corporate sentiment analysis report into Hindi:
338
+
339
+ {report}
340
+
341
+ Ensure the translation maintains professional tone and structure while accurately conveying key insights and details.
342
+ """
343
+ if model_provider == "Ollama":
344
+ translation = chat(
345
+ messages=[
346
+ {
347
+ 'role': 'user',
348
+ 'content': translation_prompt
349
+ }
350
+ ],
351
+ model='llama3.2:3b'
352
+ )
353
+ response = translation.message.content
354
+
355
+ else:
356
+ translation_llm = Groq(api_key=GROQ_API_KEY)
357
+
358
+ chat_completion = translation_llm.chat.completions.create(
359
+ messages=[
360
+ {
361
+ "role": "user",
362
+ "content": translation_prompt[:5000],
363
+ }
364
+ ],
365
+ model="llama-3.3-70b-versatile",
366
+ )
367
+ response = chat_completion.choices[0].message.content
368
+
369
+ return response
370
+
371
+
372
+ def text_to_speech(text):
373
+ url = "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb?output_format=mp3_44100_128"
374
+
375
+ model_id = "eleven_multilingual_v2"
376
+ output_file = "output.mp3"
377
+ api_key = "sk_a927222500aab9665f83f078b92e833e7ec1389ee68238c0"
378
+
379
+ headers = {
380
+ "xi-api-key": api_key,
381
+ "Content-Type": "application/json"
382
+ }
383
+
384
+ payload = {
385
+ "text": text,
386
+ "model_id": model_id
387
+ }
388
+
389
+ response = requests.post(url, headers=headers, json=payload)
390
+
391
+ if response.status_code == 200:
392
+ with open(output_file, "wb") as f:
393
+ f.write(response.content)
394
+ print(f"Audio saved to {output_file}")
395
+ else:
396
+ print(f"Error: {response.status_code} - {response.text}")
397
+
398
+ st.title("Company Sentiment Analyzer")
399
+
400
+ company_name = st.text_input("Enter Company Name", "Tesla")
401
+ model_provider = st.selectbox("Model Provider", options=["Ollama", "Groq"])
402
+
403
+ if st.button("Fetch Sentiment Data"):
404
+ web_results = fetch_from_web(company_name)
405
+
406
+ if "sources" not in web_results:
407
+ return {"error": "No sources found."}
408
+
409
+ sentiment_output = [
410
+ analyze_sentiment(article, model_provider)
411
+ for article in web_results["sources"][:5]
412
+ ]
413
+
414
+ comparative_sentiment = generate_comparative_sentiment(sentiment_output)
415
+
416
+ positive_summary, negative_summary, neutral_summary = get_summaries_by_sentiment(
417
+ sentiment_output
418
+ )
419
+
420
+ final_report = generate_final_report(
421
+ positive_summary,
422
+ negative_summary,
423
+ neutral_summary,
424
+ comparative_sentiment,
425
+ model_provider,
426
+ )
427
+
428
+ hindi_translation = translate(final_report, model_provider)
429
+ audio_path = text_to_speech(hindi_translation)
430
+
431
+ output_dict = {
432
+ "company_name": company_name,
433
+ "articles": sentiment_output,
434
+ "comparative_sentiment": comparative_sentiment,
435
+ "final_report": final_report,
436
+ "hindi_translation": hindi_translation,
437
+ "audio_url": audio_path,
438
+ }
439
+
440
+ st.subheader("Company Name")
441
+ st.write(output_dict.get("company_name"))
442
+
443
+ st.subheader("Final Report")
444
+ st.write(output_dict.get("final_report"))
445
+
446
+ st.subheader("πŸ”Š Audio Output")
447
+ audio_file = "output.mp3"
448
+ if audio_file:
449
+ st.audio(audio_file)
450
+
451
+ except requests.exceptions.RequestException as e:
452
+ st.error(f"Error fetching data: {e}")
453
+