File size: 15,862 Bytes
1356971
 
 
 
52e86c4
1356971
 
52e86c4
1356971
 
 
 
 
 
 
52e86c4
1356971
52e86c4
 
c36ecc0
 
1356971
 
 
 
 
 
 
52e86c4
 
1356971
 
 
52e86c4
1356971
 
52e86c4
 
1356971
52e86c4
 
 
 
1356971
 
52e86c4
 
 
 
 
 
1356971
52e86c4
1356971
52e86c4
 
 
 
1356971
 
 
 
52e86c4
1356971
52e86c4
1356971
52e86c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1356971
52e86c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1356971
c36ecc0
 
52e86c4
c36ecc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52e86c4
c36ecc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52e86c4
c36ecc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52e86c4
c36ecc0
 
 
 
 
52e86c4
4e486ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52e86c4
 
 
4e486ad
 
 
 
 
 
52e86c4
4e486ad
52e86c4
e0143f7
4e486ad
e0143f7
 
4e486ad
e0143f7
52e86c4
 
e0143f7
 
52e86c4
e0143f7
 
 
 
4e486ad
52e86c4
4e486ad
52e86c4
 
1356971
52e86c4
1356971
52e86c4
1356971
 
 
52e86c4
 
 
 
1356971
52e86c4
 
 
 
1356971
52e86c4
 
 
1356971
52e86c4
 
 
1356971
52e86c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1356971
52e86c4
 
 
 
 
 
 
 
 
 
 
 
1356971
 
 
 
52e86c4
 
1356971
 
 
 
52e86c4
1356971
52e86c4
1356971
 
 
 
 
 
 
 
 
 
 
 
 
52e86c4
1356971
 
 
 
 
52e86c4
 
 
 
1356971
 
 
52e86c4
 
 
 
 
c36ecc0
52e86c4
 
c36ecc0
 
52e86c4
1356971
52e86c4
c36ecc0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
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)