fingpt-analyzer / app.py
Jitender1278's picture
Update app.py
c36ecc0 verified
import gradio as gr
import requests
import json
import time
import os
from typing import Dict, Optional
class FinancialAI:
def __init__(self, api_token: str):
self.api_token = api_token
self.headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
# Using models that actually work with Inference API
self.models = {
# Financial sentiment analysis models that work
"sentiment": "mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis",
"finbert": "ProsusAI/finbert"
# Removed mistral and llama as they don't work with Inference API
}
self.endpoints = {
model_type: f"https://api-inference.huggingface.co/models/{model_name}"
for model_type, model_name in self.models.items()
}
def _make_request(self, endpoint: str, payload: dict, max_retries: int = 3) -> tuple:
"""Returns (result, error_message)"""
for attempt in range(max_retries):
try:
response = requests.post(endpoint, headers=self.headers, json=payload, timeout=30)
if response.status_code == 200:
result = response.json()
return result, None
elif response.status_code == 503:
error_data = response.json()
if "estimated_time" in error_data:
wait_time = min(error_data["estimated_time"], 60)
return None, f"Model loading, estimated wait: {wait_time}s"
time.sleep(20)
continue
elif response.status_code == 401:
return None, "❌ Invalid API token"
elif response.status_code == 403:
return None, "❌ Access denied"
elif response.status_code == 429:
return None, "❌ Rate limit exceeded"
else:
return None, f"❌ HTTP {response.status_code}: {response.text[:200]}"
except requests.exceptions.Timeout:
return None, "❌ Request timeout"
except requests.exceptions.ConnectionError:
return None, "❌ Connection error"
except Exception as e:
if attempt < max_retries - 1:
time.sleep(5)
continue
return None, f"❌ Error: {str(e)}"
return None, "❌ Max retries exceeded"
def sentiment_analysis(self, text: str) -> tuple:
"""Financial sentiment analysis using FinBERT"""
payload = {"inputs": text}
result, error = self._make_request(self.endpoints["finbert"], payload)
if result:
# FinBERT returns classification results
if isinstance(result, list) and len(result) > 0:
predictions = result[0]
if isinstance(predictions, list):
# Sort by score and get top prediction
top_prediction = max(predictions, key=lambda x: x['score'])
sentiment = top_prediction['label']
confidence = top_prediction['score']
return f"{sentiment.upper()} (confidence: {confidence:.2f})", None
else:
return str(predictions), None
return None, error or "No result returned"
def financial_news_sentiment(self, text: str) -> tuple:
"""Financial news sentiment using specialized model"""
payload = {"inputs": text}
result, error = self._make_request(self.endpoints["sentiment"], payload)
if result:
if isinstance(result, list) and len(result) > 0:
predictions = result[0]
if isinstance(predictions, list):
top_prediction = max(predictions, key=lambda x: x['score'])
sentiment = top_prediction['label']
confidence = top_prediction['score']
return f"{sentiment} (confidence: {confidence:.2f})", None
return None, error or "No result returned"
def generate_analysis(self, query: str) -> tuple:
"""Generate financial analysis using real AI/LLMs"""
# Option 1: Try Groq (fast and often free)
groq_key = os.getenv("GROQ_API_KEY")
if groq_key:
try:
headers = {
"Authorization": f"Bearer {groq_key}",
"Content-Type": "application/json"
}
payload = {
"model": "llama3-8b-8192",
"messages": [
{
"role": "system",
"content": "You are an expert financial analyst. Provide concise, actionable investment analysis and recommendations."
},
{
"role": "user",
"content": f"Analyze this financial information: {query}. Provide investment insights, market sentiment, and actionable recommendations."
}
],
"max_tokens": 250,
"temperature": 0.7
}
response = requests.post(
"https://api.groq.com/openai/v1/chat/completions",
headers=headers,
json=payload,
timeout=30
)
if response.status_code == 200:
result = response.json()
ai_response = result['choices'][0]['message']['content'].strip()
return f"πŸ€– AI Analysis:\n{ai_response}", None
except Exception as e:
pass # Fall through to next option
# Option 2: Try OpenAI
openai_key = os.getenv("OPENAI_API_KEY")
if openai_key:
try:
headers = {
"Authorization": f"Bearer {openai_key}",
"Content-Type": "application/json"
}
payload = {
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "You are a financial analyst. Provide brief, actionable investment analysis."
},
{
"role": "user",
"content": f"Analyze: {query}"
}
],
"max_tokens": 200,
"temperature": 0.7
}
response = requests.post(
"https://api.openai.com/v1/chat/completions",
headers=headers,
json=payload,
timeout=30
)
if response.status_code == 200:
result = response.json()
ai_response = result['choices'][0]['message']['content'].strip()
return f"πŸ€– AI Analysis:\n{ai_response}", None
except Exception as e:
pass # Fall through to next option
# Option 3: Try Hugging Face Inference Providers
try:
from huggingface_hub import InferenceClient
client = InferenceClient(token=self.api_token)
messages = [
{
"role": "system",
"content": "You are a financial analyst. Provide brief investment analysis."
},
{
"role": "user",
"content": f"Analyze: {query}"
}
]
# Try Inference Providers
response = client.chat_completion(
messages=messages,
model="microsoft/DialoGPT-medium",
max_tokens=200,
temperature=0.7
)
if response and response.choices:
ai_response = response.choices[0].message.content.strip()
return f"πŸ€– AI Analysis:\n{ai_response}", None
except Exception as e:
pass
# If all AI options fail, return error with instructions
return None, ("❌ No AI models available. To get real AI analysis, add API keys:\n"
"β€’ GROQ_API_KEY (free at groq.com)\n"
"β€’ OPENAI_API_KEY (at openai.com)\n"
"β€’ Or use Hugging Face Inference Providers")
# Get API token - try multiple possible secret names and methods
def get_hf_token():
"""Try multiple methods to get the HF token"""
# Method 1: Environment variables
token_options = [
"HUGGINGFACE_API_TOKEN", "HF_TOKEN", "API_TOKEN", "TOKEN",
"HUGGINGFACE_TOKEN", "HF_API_TOKEN"
]
for var_name in token_options:
token = os.getenv(var_name)
if token and token != "your_huggingface_token_here":
return token
# Method 2: Try huggingface_hub
try:
from huggingface_hub import HfFolder
token = HfFolder.get_token()
if token:
return token
except:
pass
return None
API_TOKEN = get_hf_token() or "your_huggingface_token_here"
def test_token():
"""Test if the API token is working"""
# Debug: Show what token we're getting
token_preview = API_TOKEN[:8] + "..." + API_TOKEN[-4:] if len(API_TOKEN) > 12 else "TOO_SHORT"
print(f"πŸ” Debug - Token preview: {token_preview}")
print(f"πŸ” Debug - Token length: {len(API_TOKEN)}")
print(f"πŸ” Debug - Environment variables available: {list(os.environ.keys())}")
if API_TOKEN == "your_huggingface_token_here":
return "❌ Please set your Hugging Face API token! Check Spaces secrets."
if not API_TOKEN.startswith('hf_'):
return f"❌ Token should start with 'hf_' but starts with: {API_TOKEN[:3]}..."
if len(API_TOKEN) < 30:
return f"❌ Token appears too short: {len(API_TOKEN)} characters"
headers = {"Authorization": f"Bearer {API_TOKEN}"}
try:
# Use the correct whoami-v2 endpoint
response = requests.get("https://huggingface.co/api/whoami-v2", headers=headers, timeout=10)
if response.status_code == 200:
user_data = response.json()
username = user_data.get('name', 'Unknown')
return f"βœ… API token is valid! User: {username}"
elif response.status_code == 401:
return "❌ Token is invalid. Check your Spaces secret configuration."
else:
return f"❌ Token validation failed: HTTP {response.status_code} - {response.text[:100]}"
except Exception as e:
return f"❌ Token test error: {str(e)}"
financial_ai = FinancialAI(API_TOKEN)
def analyze_query(query, analysis_type):
if not query.strip():
return "Please enter a query."
# Test token first
token_status = test_token()
if "❌" in token_status:
return token_status
try:
if analysis_type == "FinBERT Sentiment":
result, error = financial_ai.sentiment_analysis(query)
return result if result else f"❌ FinBERT Sentiment failed: {error}"
elif analysis_type == "Financial News Sentiment":
result, error = financial_ai.financial_news_sentiment(query)
return result if result else f"❌ Financial News Sentiment failed: {error}"
elif analysis_type == "AI Analysis":
result, error = financial_ai.generate_analysis(query)
return result if result else f"❌ AI Analysis failed: {error}"
else: # Comprehensive
output = "πŸ” COMPREHENSIVE FINANCIAL ANALYSIS\n\n"
# FinBERT Sentiment
sentiment1, s1_error = financial_ai.sentiment_analysis(query)
if sentiment1:
output += f"πŸ“Š FinBERT Sentiment: {sentiment1}\n\n"
else:
output += f"πŸ“Š FinBERT Sentiment: Failed - {s1_error}\n\n"
# Financial News Sentiment
sentiment2, s2_error = financial_ai.financial_news_sentiment(query)
if sentiment2:
output += f"πŸ“° Financial News Sentiment: {sentiment2}\n\n"
else:
output += f"πŸ“° Financial News Sentiment: Failed - {s2_error}\n\n"
# AI Analysis
analysis, a_error = financial_ai.generate_analysis(query)
if analysis:
output += f"πŸ€– AI Analysis:\n{analysis}\n\n"
else:
output += f"πŸ€– AI Analysis: Failed - {a_error}\n\n"
return output
except Exception as e:
return f"❌ Unexpected error: {str(e)}"
# Create Gradio interface
with gr.Blocks(title="Financial AI Analysis Platform") as demo:
gr.Markdown("# 🏦 Financial AI Analysis Platform")
gr.Markdown("Get AI-powered financial insights using working Hugging Face models for sentiment analysis and market insights.")
# Add token status display
with gr.Row():
token_status = gr.Textbox(
label="πŸ”‘ Token Status",
value=test_token(),
interactive=False
)
with gr.Row():
with gr.Column():
query_input = gr.Textbox(
label="πŸ” Enter your financial query or news",
placeholder="e.g., 'Apple stock is performing well' or 'Tesla reports strong earnings'",
lines=3
)
analysis_type = gr.Dropdown(
choices=["FinBERT Sentiment", "Financial News Sentiment", "AI Analysis", "Comprehensive"],
label="πŸ“Š Analysis Type",
value="FinBERT Sentiment"
)
analyze_btn = gr.Button("πŸš€ Analyze", variant="primary")
with gr.Column():
output = gr.Textbox(
label="πŸ’‘ Analysis Results",
lines=12,
interactive=False
)
analyze_btn.click(
fn=analyze_query,
inputs=[query_input, analysis_type],
outputs=output
)
gr.Examples(
examples=[
["Apple stock is performing exceptionally well this quarter", "FinBERT Sentiment"],
["Tesla reports disappointing earnings, stock falls", "Financial News Sentiment"],
["Microsoft investment potential for 2025", "AI Analysis"],
["Amazon announces major expansion plans", "Comprehensive"]
],
inputs=[query_input, analysis_type]
)
gr.Markdown("""
### πŸ“ Models Used:
- **FinBERT**: ProsusAI/finbert (financial sentiment analysis)
- **Financial News**: mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis
- **AI Analysis**: Real LLMs via Hugging Face Inference Providers
### ⚠️ Note:
AI Analysis uses actual language models for genuine AI-generated insights.
If LLMs are unavailable, you can also integrate with OpenAI, Anthropic, or other providers.
""")
if __name__ == "__main__":
demo.launch(share=True)