Ani-404 commited on
Commit
cef75fc
·
1 Parent(s): 458ef39

Switch to HuggingFace Inference API - no local model loading

Browse files
Files changed (2) hide show
  1. app.py +123 -151
  2. requirements.txt +0 -0
app.py CHANGED
@@ -17,6 +17,8 @@ import torch
17
  import plotly.express as px
18
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
19
  from streamlit_option_menu import option_menu
 
 
20
  import os
21
  import sys
22
  from pathlib import Path
@@ -24,7 +26,7 @@ import yfinance as yf
24
  from datetime import datetime, timedelta
25
  import warnings
26
  warnings.filterwarnings('ignore')
27
- from huggingface_hub import snapshot_download
28
 
29
  # Get project root dynamically (no hardcoded paths!)
30
  PROJECT_ROOT = Path(__file__).parent
@@ -44,142 +46,42 @@ EMOTION_EMOJIS = {
44
  "neutral": "😐", "sadness": "😔", "shame": "😳", "surprise": "😮"
45
  }
46
 
47
- import threading
48
- import shutil
49
- import time
50
-
51
- # Global download status tracking
52
- if 'models_downloading' not in st.session_state:
53
- st.session_state.models_downloading = False
54
- if 'models_ready' not in st.session_state:
55
- st.session_state.models_ready = False
56
- if 'download_progress' not in st.session_state:
57
- st.session_state.download_progress = ""
58
- if 'download_error' not in st.session_state:
59
- st.session_state.download_error = None
60
-
61
- def download_models_background():
62
- """Download models in background thread - doesn't block app startup"""
63
- try:
64
- emotion_dir = PROJECT_ROOT / "Models" / "sentiment_model_distilbert"
65
- finbert_dir = PROJECT_ROOT / "finance" / "finbert_large_emotion_model"
66
-
67
- # Download emotion model
68
- if not emotion_dir.exists() or not (emotion_dir / "model.safetensors").exists():
69
- st.session_state.download_progress = "📥 Downloading emotion model..."
70
- temp_dir = snapshot_download("Ani-404/emotion-model")
71
- emotion_dir.parent.mkdir(parents=True, exist_ok=True)
72
- if emotion_dir.exists():
73
- shutil.rmtree(emotion_dir)
74
- shutil.move(temp_dir, emotion_dir)
75
-
76
- # Download financial model
77
- if not finbert_dir.exists() or not (finbert_dir / "model.safetensors").exists():
78
- st.session_state.download_progress = "📥 Downloading financial model..."
79
- temp_dir = snapshot_download("Ani-404/finbert-model")
80
- finbert_dir.parent.mkdir(parents=True, exist_ok=True)
81
- if finbert_dir.exists():
82
- shutil.rmtree(finbert_dir)
83
- shutil.move(temp_dir, finbert_dir)
84
-
85
- # Success!
86
- st.session_state.download_progress = "✅ Your trained models are now ready!"
87
- st.session_state.models_ready = True
88
- st.session_state.models_downloading = False
89
-
90
- except Exception as e:
91
- st.session_state.download_error = str(e)
92
- st.session_state.download_progress = f"❌ Download failed: {str(e)[:100]}"
93
- st.session_state.models_downloading = False
94
-
95
-
96
- def check_and_start_download():
97
- """Check if models exist, start background download if needed"""
98
- emotion_dir = PROJECT_ROOT / "Models" / "sentiment_model_distilbert"
99
- finbert_dir = PROJECT_ROOT / "finance" / "finbert_large_emotion_model"
100
-
101
- # Check if models already exist
102
- emotion_exists = emotion_dir.exists() and (emotion_dir / "model.safetensors").exists()
103
- finbert_exists = finbert_dir.exists() and (finbert_dir / "model.safetensors").exists()
104
-
105
- if emotion_exists and finbert_exists:
106
- st.session_state.models_ready = True
107
- st.session_state.download_progress = "✅ Your trained models are ready!"
108
- return
109
-
110
- # Start download in background if not already downloading
111
- if not st.session_state.models_downloading and not st.session_state.models_ready:
112
- st.session_state.models_downloading = True
113
- st.session_state.download_progress = "🚀 Starting model download..."
114
-
115
- # Start background thread (daemon=True means it won't block app shutdown)
116
- download_thread = threading.Thread(target=download_models_background, daemon=True)
117
- download_thread.start()
118
-
119
-
120
- def show_download_status():
121
- """Show download status without blocking the app"""
122
- if st.session_state.models_downloading:
123
- # Show progress WITHOUT sleep/rerun (these block the app)
124
- col1, col2 = st.columns([3, 1])
125
- with col1:
126
- st.info(f"🔄 {st.session_state.download_progress}")
127
- with col2:
128
- if st.button("🔄 Refresh Status"):
129
- st.rerun()
130
-
131
- st.info("💡 **App is fully functional** with high-quality fallback models!")
132
- st.caption("⏱️ Downloads happen in background. Click refresh to check progress.")
133
-
134
- elif st.session_state.models_ready:
135
- st.success("🎉 **Upgrade Complete!** Now using your trained models.")
136
-
137
- elif st.session_state.download_error:
138
- st.warning(f"⚠️ Download issue: {st.session_state.download_error[:100]}")
139
- st.info("📊 **App running perfectly** with professional fallback models.")
140
-
141
- else:
142
- st.info("🚀 **App ready!** Using high-quality fallback models.")
143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
- check_and_start_download()
146
- show_download_status()
147
 
148
 
149
  @st.cache_resource
150
  def load_models():
151
- """Load both emotion and financial models with fallbacks"""
152
  models = {}
153
-
154
- try:
155
- # Load General Emotion Model (existing path)
156
- emotion_model_path = PROJECT_ROOT / "Models" / "sentiment_model_distilbert"
157
- if emotion_model_path.exists():
158
- models['general_tokenizer'] = AutoTokenizer.from_pretrained(str(emotion_model_path))
159
- models['general_model'] = AutoModelForSequenceClassification.from_pretrained(str(emotion_model_path))
160
- st.sidebar.success("Emotion model loaded")
161
- else:
162
- st.sidebar.warning("Emotion model not found - using demo mode")
163
- models['general_tokenizer'] = None
164
- models['general_model'] = None
165
-
166
- # Load Financial Model (existing path)
167
- finbert_path = PROJECT_ROOT / "finance" / "finbert_large_emotion_model"
168
- if finbert_path.exists():
169
- models['finbert_tokenizer'] = AutoTokenizer.from_pretrained(str(finbert_path))
170
- models['finbert_model'] = AutoModelForSequenceClassification.from_pretrained(str(finbert_path))
171
- st.sidebar.success("FinBERT model loaded")
172
- else:
173
- st.sidebar.warning("FinBERT model not found - using demo mode")
174
- models['finbert_tokenizer'] = None
175
- models['finbert_model'] = None
176
-
177
- except Exception as e:
178
- st.sidebar.error(f"Error loading models: {e}")
179
  return None
180
 
181
- return models
182
-
183
  def predict_emotions_demo(text):
184
  """Demo emotion prediction using keyword analysis"""
185
  text_lower = text.lower()
@@ -213,33 +115,104 @@ def predict_emotions_demo(text):
213
  return emotion, confidence, probs
214
 
215
  def predict_emotions_real(text, model, tokenizer):
216
- """Real emotion prediction using trained model"""
217
- inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True, max_length=512)
218
-
219
- with torch.no_grad():
220
- outputs = model(**inputs)
221
- predictions = torch.argmax(outputs.logits, dim=1)
222
- probabilities = torch.nn.functional.softmax(outputs.logits, dim=1)
223
-
224
- emotion = model.config.id2label[predictions.item()]
225
- confidence = float(probabilities.max().item())
226
- probs = probabilities.numpy()[0].tolist()
227
-
228
- return emotion, confidence, probs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
  def analyze_financial_sentiment(text, ticker=None):
231
- """Analyze financial sentiment with trading signal"""
232
  if not text.strip():
233
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
- # Simple keyword-based analysis
 
 
236
  text_lower = text.lower()
237
  positive_words = ['growth', 'profit', 'exceeded', 'strong', 'robust', 'expansion', 'record', 'beat', 'success', 'revenue']
238
  negative_words = ['decline', 'loss', 'weak', 'concern', 'challenge', 'disruption', 'headwinds', 'drop', 'fall', 'miss']
239
-
240
  pos_count = sum(1 for word in positive_words if word in text_lower)
241
  neg_count = sum(1 for word in negative_words if word in text_lower)
242
-
243
  if pos_count > neg_count:
244
  sentiment_score = 0.3 + (pos_count - neg_count) * 0.15
245
  signal = "BUY" if sentiment_score > 0.5 else "HOLD"
@@ -249,18 +222,17 @@ def analyze_financial_sentiment(text, ticker=None):
249
  else:
250
  sentiment_score = 0.0
251
  signal = "HOLD"
252
-
253
- # Clamp between -1 and 1
254
  sentiment_score = max(-1.0, min(1.0, sentiment_score))
255
-
256
- # Get stock data if ticker provided
257
  stock_data = None
258
  if ticker:
259
  try:
260
  stock = yf.Ticker(ticker)
261
  hist = stock.history(period="5d")
262
  if len(hist) >= 2:
263
- recent_change = (hist['Close'][-1] - hist['Close'][-2]) / hist['Close'][-2] * 100
264
  stock_data = {
265
  'ticker': ticker,
266
  'price': hist['Close'][-1],
@@ -268,7 +240,7 @@ def analyze_financial_sentiment(text, ticker=None):
268
  }
269
  except Exception as e:
270
  st.warning(f"Could not fetch stock data for {ticker}: {e}")
271
-
272
  return {
273
  'sentiment_score': sentiment_score,
274
  'signal': signal,
 
17
  import plotly.express as px
18
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
19
  from streamlit_option_menu import option_menu
20
+ from huggingface_hub import InferenceClient
21
+ import requests
22
  import os
23
  import sys
24
  from pathlib import Path
 
26
  from datetime import datetime, timedelta
27
  import warnings
28
  warnings.filterwarnings('ignore')
29
+
30
 
31
  # Get project root dynamically (no hardcoded paths!)
32
  PROJECT_ROOT = Path(__file__).parent
 
46
  "neutral": "😐", "sadness": "😔", "shame": "😳", "surprise": "😮"
47
  }
48
 
49
+ # HuggingFace Inference API Setup
50
+ HF_TOKEN = st.secrets.get("HF_TOKEN")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ @st.cache_resource
53
+ def get_inference_clients():
54
+ """Initialize HuggingFace Inference clients"""
55
+ try:
56
+ emotion_client = InferenceClient(
57
+ model="Ani-404/emotion-model", # Your actual model name
58
+ token=HF_TOKEN
59
+ )
60
+ finbert_client = InferenceClient(
61
+ model="Ani-404/finbert-model", # Your actual model name
62
+ token=HF_TOKEN
63
+ )
64
+ return emotion_client, finbert_client
65
+ except:
66
+ return None, None
67
 
68
+ emotion_client, finbert_client = get_inference_clients()
 
69
 
70
 
71
  @st.cache_resource
72
  def load_models():
73
+ """Check if HF Inference API is available"""
74
  models = {}
75
+
76
+ if emotion_client and finbert_client:
77
+ models['general_model'] = "hf_api"
78
+ models['finbert_model'] = "hf_api"
79
+ st.sidebar.success("✅ Using your trained models via HuggingFace API")
80
+ return models
81
+ else:
82
+ st.sidebar.warning("⚠️ Using demo mode - add HF_TOKEN to secrets")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  return None
84
 
 
 
85
  def predict_emotions_demo(text):
86
  """Demo emotion prediction using keyword analysis"""
87
  text_lower = text.lower()
 
115
  return emotion, confidence, probs
116
 
117
  def predict_emotions_real(text, model, tokenizer):
118
+ """Use HuggingFace Inference API for emotion prediction"""
119
+ try:
120
+ # Call your emotion model via API
121
+ response = emotion_client.text_classification(text)
122
+
123
+ if response and len(response) > 0:
124
+ # Get top prediction
125
+ top_pred = max(response, key=lambda x: x['score'])
126
+ emotion = top_pred['label'].lower()
127
+ confidence = top_pred['score']
128
+
129
+ # Create probability distribution
130
+ emotion_labels = ['anger', 'disgust', 'fear', 'joy', 'neutral', 'sadness', 'shame', 'surprise']
131
+ probs = [0.1] * len(emotion_labels) # Default small probabilities
132
+
133
+ # Set the predicted emotion's probability
134
+ if emotion in emotion_labels:
135
+ idx = emotion_labels.index(emotion)
136
+ probs[idx] = confidence
137
+
138
+ return emotion, confidence, probs
139
+ else:
140
+ # Fallback to demo mode
141
+ return predict_emotions_demo(text)
142
+
143
+ except Exception as e:
144
+ st.error(f"API Error: {e}")
145
+ return predict_emotions_demo(text)
146
 
147
  def analyze_financial_sentiment(text, ticker=None):
148
+ """Use HuggingFace Inference API for financial sentiment"""
149
  if not text.strip():
150
  return None
151
+
152
+ try:
153
+ # Call your finbert model via API
154
+ response = finbert_client.text_classification(text)
155
+
156
+ if response and len(response) > 0:
157
+ top_pred = max(response, key=lambda x: x['score'])
158
+ label = top_pred['label'].lower()
159
+ confidence = top_pred['score']
160
+
161
+ # Map to trading signals
162
+ if label == 'positive':
163
+ signal = "BUY" if confidence > 0.7 else "HOLD"
164
+ sentiment_score = confidence
165
+ elif label == 'negative':
166
+ signal = "SELL" if confidence > 0.7 else "HOLD"
167
+ sentiment_score = -confidence
168
+ else:
169
+ signal = "HOLD"
170
+ sentiment_score = 0.0
171
+
172
+ else:
173
+ # Fallback to keyword analysis
174
+ return analyze_financial_sentiment_demo(text, ticker)
175
+
176
+ except Exception as e:
177
+ st.error(f"Financial API Error: {e}")
178
+ return analyze_financial_sentiment_demo(text, ticker)
179
+
180
+ # Get stock data if ticker provided
181
+ stock_data = None
182
+ if ticker:
183
+ try:
184
+ stock = yf.Ticker(ticker)
185
+ hist = stock.history(period="5d")
186
+ if len(hist) >= 2:
187
+ recent_change = ((hist['Close'][-1] - hist['Close'][-2]) / hist['Close'][-2]) * 100
188
+ stock_data = {
189
+ 'ticker': ticker,
190
+ 'price': hist['Close'][-1],
191
+ 'change': recent_change
192
+ }
193
+ except Exception as e:
194
+ st.warning(f"Could not fetch stock data for {ticker}: {e}")
195
+
196
+ return {
197
+ 'sentiment_score': sentiment_score,
198
+ 'signal': signal,
199
+ 'confidence': confidence,
200
+ 'stock_data': stock_data,
201
+ 'positive_ratio': max(0, sentiment_score),
202
+ 'negative_ratio': max(0, -sentiment_score),
203
+ 'neutral_ratio': 1 - abs(sentiment_score)
204
+ }
205
 
206
+ def analyze_financial_sentiment_demo(text, ticker=None):
207
+ """Original keyword-based analysis as fallback"""
208
+ # Your existing keyword analysis code here
209
  text_lower = text.lower()
210
  positive_words = ['growth', 'profit', 'exceeded', 'strong', 'robust', 'expansion', 'record', 'beat', 'success', 'revenue']
211
  negative_words = ['decline', 'loss', 'weak', 'concern', 'challenge', 'disruption', 'headwinds', 'drop', 'fall', 'miss']
212
+
213
  pos_count = sum(1 for word in positive_words if word in text_lower)
214
  neg_count = sum(1 for word in negative_words if word in text_lower)
215
+
216
  if pos_count > neg_count:
217
  sentiment_score = 0.3 + (pos_count - neg_count) * 0.15
218
  signal = "BUY" if sentiment_score > 0.5 else "HOLD"
 
222
  else:
223
  sentiment_score = 0.0
224
  signal = "HOLD"
225
+
 
226
  sentiment_score = max(-1.0, min(1.0, sentiment_score))
227
+
228
+ # Get stock data
229
  stock_data = None
230
  if ticker:
231
  try:
232
  stock = yf.Ticker(ticker)
233
  hist = stock.history(period="5d")
234
  if len(hist) >= 2:
235
+ recent_change = ((hist['Close'][-1] - hist['Close'][-2]) / hist['Close'][-2]) * 100
236
  stock_data = {
237
  'ticker': ticker,
238
  'price': hist['Close'][-1],
 
240
  }
241
  except Exception as e:
242
  st.warning(f"Could not fetch stock data for {ticker}: {e}")
243
+
244
  return {
245
  'sentiment_score': sentiment_score,
246
  'signal': signal,
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ