File size: 7,936 Bytes
325cb74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""

pipeline/key_validator.py -- Validate API keys before running analysis.

"""
from __future__ import annotations
import sys
from pathlib import Path
from typing import Tuple

sys.path.insert(0, str(Path(__file__).parent.parent))


def validate_fmp(key: str) -> Tuple[bool, str]:
    """Test FMP key with a lightweight profile call."""
    if not key or key.strip() in ("", "your_fmp_key_here"):
        return False, "No key entered."
    try:
        import requests
        r = requests.get(
            "https://financialmodelingprep.com/api/v3/profile/AAPL",
            params={"apikey": key.strip()},
            timeout=10,
        )
        data = r.json()
        if isinstance(data, list) and data and data[0].get("symbol"):
            return True, f"Valid [OK]  (plan: {data[0].get('exchange','FMP')})"
        if isinstance(data, dict) and "Error Message" in data:
            return False, f"Invalid key: {data['Error Message']}"
        return False, "Unexpected response -- key may be invalid."
    except Exception as e:
        return False, f"Connection error: {e}"


def validate_gemini(key: str) -> Tuple[bool, str]:
    """Test Gemini key with a minimal generate call."""
    if not key or key.strip() in ("", "your_gemini_key_here"):
        return False, "No key entered."
    try:
        # Try new google.genai SDK first
        from google import genai
        client = genai.Client(api_key=key.strip())
        resp = client.models.generate_content(
            model="gemini-2.0-flash",
            contents="Reply with the single word: OK",
        )
        text = resp.text.strip() if resp.text else ""
        return True, f"Valid [OK]  (response: '{text[:20]}')"
    except Exception as e1:
        # Fallback to old SDK
        try:
            import google.generativeai as genai_old
            genai_old.configure(api_key=key.strip())
            model = genai_old.GenerativeModel("gemini-1.5-flash")
            resp = model.generate_content("Reply with the single word: OK")
            return True, f"Valid [OK]  (response: '{resp.text.strip()[:20]}')"
        except Exception as e2:
            msg = str(e1)
            if "API_KEY_INVALID" in msg or "invalid" in msg.lower():
                return False, "Invalid API key. Check and re-enter."
            if "quota" in msg.lower():
                return True, "Key valid but quota exceeded -- will still work."
            return False, f"Error: {msg[:80]}"


def validate_claude(key: str) -> Tuple[bool, str]:
    """Test Claude key with a minimal message."""
    if not key or key.strip() in ("", "your_claude_key_here"):
        return False, "No key entered."
    try:
        import anthropic
        client = anthropic.Anthropic(api_key=key.strip())
        msg = client.messages.create(
            model="claude-3-5-haiku-20241022",
            max_tokens=10,
            messages=[{"role": "user", "content": "Reply with the single word: OK"}],
        )
        text = msg.content[0].text.strip() if msg.content else ""
        return True, f"Valid [OK]  (response: '{text[:20]}')"
    except Exception as e:
        msg = str(e)
        if "authentication" in msg.lower() or "401" in msg or "invalid" in msg.lower():
            return False, "Invalid API key. Check and re-enter."
        if "quota" in msg.lower() or "credit" in msg.lower():
            return True, "Key valid but credit exhausted."
        return False, f"Error: {msg[:80]}"


def validate_openai(key: str) -> Tuple[bool, str]:
    """Test OpenAI key with a minimal chat completion."""
    if not key or key.strip() in ("", "your_openai_key_here"):
        return False, "No key entered."
    try:
        from openai import OpenAI
        client = OpenAI(api_key=key.strip())
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": "Reply with the single word: OK"}],
            max_tokens=5,
        )
        text = resp.choices[0].message.content.strip() if resp.choices else ""
        return True, f"Valid [OK]  (response: '{text[:20]}')"
    except Exception as e:
        msg = str(e)
        if "401" in msg or "Incorrect API key" in msg or "invalid_api_key" in msg:
            return False, "Invalid API key. Check and re-enter."
        if "quota" in msg.lower() or "insufficient" in msg.lower():
            return True, "Key valid but quota/credit exhausted."
        return False, f"Error: {msg[:80]}"


def validate_groq(key: str) -> tuple:
    """Test Groq key with a minimal chat completion (free API)."""
    if not key or key.strip() in ("", "your_groq_key_here"):
        return False, "No key entered."
    try:
        from openai import OpenAI
        client = OpenAI(
            api_key=key.strip(),
            base_url="https://api.groq.com/openai/v1",
        )
        resp = client.chat.completions.create(
            model="llama-3.3-70b-versatile",
            messages=[{"role": "user", "content": "Reply with the single word: OK"}],
            max_tokens=5,
        )
        text = resp.choices[0].message.content.strip() if resp.choices else ""
        return True, f"Valid [OK]  (Llama 3.3 70B, response: '{text[:20]}')"
    except Exception as e:
        msg = str(e)
        if "401" in msg or "invalid_api_key" in msg.lower() or "unauthorized" in msg.lower():
            return False, "Invalid Groq API key. Check and re-enter."
        if "quota" in msg.lower() or "rate" in msg.lower():
            return True, "Key valid but rate-limited -- try again shortly."
        return False, f"Error: {msg[:80]}"


def validate_nvidia(key: str) -> tuple:
    """Test NVIDIA NIM key with a minimal chat completion."""
    if not key or key.strip() in ("", "your_nvidia_key_here"):
        return False, "No key entered."
    try:
        from openai import OpenAI
        client = OpenAI(
            api_key=key.strip(),
            base_url="https://integrate.api.nvidia.com/v1",
        )
        resp = client.chat.completions.create(
            model="meta/llama-3.1-405b-instruct",
            messages=[{"role": "user", "content": "Reply with the single word: OK"}],
            max_tokens=5,
        )
        text = resp.choices[0].message.content.strip() if resp.choices else ""
        return True, f"Valid [OK]  (NVIDIA NIM, response: '{text[:20]}')"
    except Exception as e:
        msg = str(e)
        if "401" in msg or "invalid_api_key" in msg.lower() or "unauthorized" in msg.lower():
            return False, "Invalid NVIDIA API key. Check and re-enter."
        return False, f"Error: {msg[:80]}"


def check_any_ai_model(gemini_key: str = "", claude_key: str = "", openai_key: str = "", groq_key: str = "", nvidia_key: str = "") -> tuple:
    """Quickly check if at least one AI model is usable."""
    checks = [
        ("nvidia", nvidia_key, validate_nvidia),
        ("groq",   groq_key,   validate_groq),
        ("gemini", gemini_key, validate_gemini),
        ("claude", claude_key, validate_claude),
        ("openai", openai_key, validate_openai),
    ]
    for name, key, fn in checks:
        if key and key.strip():
            ok, msg = fn(key)
            if ok:
                return True, f"{name.capitalize()}: {msg}"

    return False, (
        "No working AI model key found.\n\n"
        "Free options (no credit card needed):\n"
        "  • NVIDIA NIM (Free credits) -> build.nvidia.com\n"
        "  • Groq (Llama 3.3 70B) -> console.groq.com\n"
        "  • Gemini -> aistudio.google.com\n\n"
        "Paid options:\n"
        "  • Claude -> console.anthropic.com\n"
        "  • ChatGPT -> platform.openai.com/api-keys\n\n"
        "OR uncheck all AI models to run data-only analysis."
    )