Company-Sentiment / utils.py
sidmanale643's picture
Upload 9 files
adf70a1 verified
import os
from typing import Literal, List
from tavily import TavilyClient
from pydantic import BaseModel
from ollama import chat
from dotenv import load_dotenv
from groq import Groq
import instructor
import requests
GROQ_API_KEY = "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"
ELEVEN_LABS_API_KEY = "sk_a927222500aab9665f83f078b92e833e7ec1389ee68238c0"
TAVILY_API_KEY = "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"
load_dotenv()
def fetch_from_web(query):
tavily_client = TavilyClient(api_key=TAVILY_API_KEY)
response = tavily_client.search(
query,
include_raw_content=True,
max_results=10,
topic="news",
search_depth="basic"
)
return {"sources": response['results']}
class Sentiment(BaseModel):
summary: str
reasoning: str
topics: List[str]
sentiment: Literal['positive', 'negative', 'neutral']
def analyze_sentiment(article, model_provider):
sentiment_prompt = f"""
Analyze the following news article about a company:
1. **Summary**: Provide a comprehensive summary of the article's key points.
2. **Sentiment Analysis**:
- Classify the overall sentiment toward the company as: POSITIVE, NEGATIVE, or NEUTRAL
- Support your classification with specific quotes, tone analysis, and factual evidence from the article
- Explain your reasoning for this sentiment classification in 2 to 3 lines.
3. **Key Topics**:
- Identify 3-5 main topics discussed in the article
- Only give the name of the topics
Be as detailed and objective as possible in your reasoning.
Article Title: {article['title']}
Article: {article['raw_content']}
"""
try:
if model_provider == "Ollama":
response = chat(
messages=[
{
'role': 'user',
'content': sentiment_prompt
}
],
model='llama3.2:3b',
format=Sentiment.model_json_schema(),
)
sentiment_output = Sentiment.model_validate_json(response.message.content)
final_dict = {
"title": article["title"],
"summary": sentiment_output.summary,
"reasoning": sentiment_output.reasoning,
"topics": sentiment_output.topics,
"sentiment": sentiment_output.sentiment
}
else:
llm = Groq(api_key=GROQ_API_KEY)
llm = instructor.from_groq(llm, mode=instructor.Mode.TOOLS)
resp = llm.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[
{
"role": "user",
"content": sentiment_prompt,
}
],
response_model=Sentiment,
)
sentiment_output = resp.model_dump()
final_dict = {
"title": article["title"],
"summary": sentiment_output.get("summary"),
"reasoning": sentiment_output.get("reasoning"),
"topics": sentiment_output.get("topics"),
"sentiment": sentiment_output.get("sentiment")
}
return final_dict
except Exception as e:
print(f"Error parsing sentiment output: {e}")
return None
def generate_comparative_sentiment(articles):
sentiment_counts = {"Positive": 0, "Negative": 0, "Neutral": 0}
for article in articles:
sentiment = article.get("sentiment", "").lower()
if sentiment == "positive":
sentiment_counts["Positive"] += 1
elif sentiment == "negative":
sentiment_counts["Negative"] += 1
elif sentiment == "neutral":
sentiment_counts["Neutral"] += 1
all_topics = []
for article in articles:
all_topics.extend(article.get("topics", []))
unique_topics = set(all_topics)
topic_counts = {}
for topic in unique_topics:
count = all_topics.count(topic)
topic_counts[topic] = count
common_topics = [topic for topic, count in topic_counts.items() if count > 1]
unique_topics = {}
for i, article in enumerate(articles):
article_topics = set(article.get("topics", []))
for j, other_article in enumerate(articles):
if i != j:
other_topics = set(other_article.get("topics", []))
unique_topics[f"Unique Topics in Article {i+1}"] = list(article_topics - other_topics)
comparative_sentiment = {
"Sentiment Distribution": sentiment_counts,
"Coverage Differences": "coverage_differences",
"Topic Overlap": {
"Common Topics": common_topics,
"Unique Topics in Article 1": unique_topics.get("Unique Topics in Article 1", []),
"Unique Topics in Article 2": unique_topics.get("Unique Topics in Article 2", []),
"Unique Topics in Article 3": unique_topics.get("Unique Topics in Article 3", []),
"Unique Topics in Article 4": unique_topics.get("Unique Topics in Article 4", []),
"Unique Topics in Article 5": unique_topics.get("Unique Topics in Article 5", []),
"Unique Topics in Article 6": unique_topics.get("Unique Topics in Article 6", []),
"Unique Topics in Article 7": unique_topics.get("Unique Topics in Article 7", []),
"Unique Topics in Article 8": unique_topics.get("Unique Topics in Article 8", []),
"Unique Topics in Article 9": unique_topics.get("Unique Topics in Article 9", []),
"Unique Topics in Article 10": unique_topics.get("Unique Topics in Article 10", [])
},
}
return comparative_sentiment
def get_summaries_by_sentiment(articles):
pos_sum = []
neg_sum = []
neutral_sum = []
for article in articles:
sentiment = article.get("sentiment", "").lower()
title = article.get("title", "No Title")
summary = article.get("summary", "No Summary")
article_text = f'Title: {title}\nSummary: {summary}'
if sentiment == "positive":
pos_sum.append(article_text)
elif sentiment == "negative":
neg_sum.append(article_text)
elif sentiment == "neutral":
neutral_sum.append(article_text)
pos_sum = "\n\n".join(pos_sum) if pos_sum else "No positive articles available."
neg_sum = "\n\n".join(neg_sum) if neg_sum else "No negative articles available."
neutral_sum = "\n\n".join(neutral_sum) if neutral_sum else "No neutral articles available."
return pos_sum, neg_sum, neutral_sum
def comparative_analysis(pos_sum, neg_sum, neutral_sum, model_provider):
prompt = f"""
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:
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.
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?
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?
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?
5. **Market or Stakeholder Impact**: Discuss potential effects on stakeholders (e.g., investors, customers, regulators) based on the sentiment of each article type.
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?
### Positive Articles:
{pos_sum}
### Negative Articles:
{neg_sum}
### Neutral Articles:
{neutral_sum}
"""
if model_provider == "Ollama":
response = chat(
messages=[
{
'role': 'user',
'content': prompt
}
],
model='llama3.2:3b'
)
response = response.message.content
else:
llm = Groq(api_key=GROQ_API_KEY)
chat_completion = llm.chat.completions.create(
messages=[
{
"role": "user",
"content": prompt[:5000],
}
],
model="llama-3.3-70b-versatile",
)
response = chat_completion.choices[0].message.content
return response
def generate_final_report(pos_sum, neg_sum, neutral_sum, comparative_sentiment, model_provider):
final_report_prompt = f"""
Corporate News Sentiment Analysis Report:
### 1. Executive Summary
- Overview of sentiment distribution: {comparative_sentiment["Sentiment Distribution"]['Positive']} positive, {comparative_sentiment["Sentiment Distribution"]['Negative']} negative, {comparative_sentiment["Sentiment Distribution"]['Neutral']} neutral.
- Highlight the dominant narrative shaping the company's perception.
- Summarize key drivers behind positive and negative sentiments.
### 2. Media Coverage Analysis
- Identify major news sources covering the company.
- Highlight patterns in coverage across platforms (e.g., frequency, timing).
- Identify whether media sentiment shifts over time.
### 3. Sentiment Breakdown
- **Positive Sentiment:**
* Titles and sources: {pos_sum}
* Key themes, notable quotes, and focal areas (e.g., product, leadership).
- **Negative Sentiment:**
* Titles and sources: {neg_sum}
* Key themes, notable quotes, and areas of concern.
- **Neutral Sentiment:**
* Titles and sources: {neutral_sum}
* Key themes and neutral narratives.
### 4. Narrative Analysis
- Identify primary storylines about the company.
- Analyze how the company is positioned (positive, neutral, negative).
- Detect shifts or emerging narratives over time.
### 5. Key Drivers of Sentiment
- Identify specific events, announcements, or actions driving media sentiment.
- Evaluate sentiment linked to industry trends vs. company-specific factors.
- Highlight company strengths and weaknesses based on media portrayal.
### 6. Competitive Context
- Identify competitor comparisons.
- Analyze how media sentiment about the company compares to industry standards.
- Highlight competitive advantages or concerns raised by the media.
### 7. Stakeholder Perspective
- Identify how key stakeholders (e.g., investors, customers, regulators) are represented.
- Analyze stakeholder concerns and reputation risks/opportunities.
### 8. Recommendations
- Suggest strategies to mitigate negative sentiment.
- Recommend approaches to amplify positive narratives.
- Provide messaging suggestions for future announcements.
### 9. Appendix
- Full article details (title, publication, date, author, URL).
- Sentiment scoring methodology.
- Media monitoring metrics (reach, engagement, etc.).
"""
if model_provider == "Ollama":
final_report = chat(
messages=[
{
'role': 'user',
'content': final_report_prompt
}
],
model='llama3.2:3b'
)
response = final_report.message.content
else:
llm = Groq(api_key=GROQ_API_KEY)
chat_completion = llm.chat.completions.create(
messages=[
{
"role": "user",
"content": final_report_prompt[:5000],
}
],
model="llama-3.3-70b-versatile",
)
response = chat_completion.choices[0].message.content
return response
def translate(report, model_provider):
translation_prompt = f"""
Translate the following corporate sentiment analysis report into Hindi:
{report}
Ensure the translation maintains professional tone and structure while accurately conveying key insights and details.
"""
if model_provider == "Ollama":
translation = chat(
messages=[
{
'role': 'user',
'content': translation_prompt
}
],
model='llama3.2:3b'
)
response = translation.message.content
else:
translation_llm = Groq(api_key=GROQ_API_KEY)
chat_completion = translation_llm.chat.completions.create(
messages=[
{
"role": "user",
"content": translation_prompt[:5000],
}
],
model="llama-3.3-70b-versatile",
)
response = chat_completion.choices[0].message.content
return response
def text_to_speech(text):
url = "https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb?output_format=mp3_44100_128"
model_id = "eleven_multilingual_v2"
output_file = "output.mp3"
api_key = "sk_a927222500aab9665f83f078b92e833e7ec1389ee68238c0"
headers = {
"xi-api-key": api_key,
"Content-Type": "application/json"
}
payload = {
"text": text,
"model_id": model_id
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
with open(output_file, "wb") as f:
f.write(response.content)
print(f"Audio saved to {output_file}")
else:
print(f"Error: {response.status_code} - {response.text}")
#