File size: 7,773 Bytes
c426d0d
684eb72
 
7e83507
aff0db3
 
 
 
684eb72
aff0db3
 
 
c426d0d
aff0db3
 
 
 
 
7e83507
 
 
 
a7b6807
aff0db3
 
c426d0d
aff0db3
743d09e
 
7e83507
743d09e
aff0db3
 
684eb72
aff0db3
9bcbc93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c426d0d
 
aff0db3
684eb72
 
9bcbc93
 
 
 
 
 
 
684eb72
9e8adcf
 
684eb72
9e8adcf
 
 
 
 
 
 
684eb72
 
 
c426d0d
9e8adcf
 
 
684eb72
9e8adcf
684eb72
9e8adcf
 
 
 
684eb72
 
 
 
9e8adcf
 
684eb72
 
 
9e8adcf
 
684eb72
 
9e8adcf
 
 
 
684eb72
 
9e8adcf
 
684eb72
9e8adcf
 
684eb72
 
 
 
 
9e8adcf
 
 
 
684eb72
 
 
 
 
aff0db3
684eb72
 
 
5e79371
a7b6807
c426d0d
aff0db3
684eb72
9bcbc93
 
 
 
 
 
 
7e83507
9bcbc93
 
 
7e83507
 
 
 
 
 
 
 
 
 
 
 
684eb72
7e83507
a7b6807
 
 
7e83507
5e79371
 
 
 
 
 
684eb72
 
5e79371
 
 
 
684eb72
 
 
7e83507
 
684eb72
7e83507
 
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
import time
import json
import requests
import traceback
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

app = FastAPI(title="Gemini AI Dashboard API")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

API_KEYS = [
    "AIzaSyAIo-DJ1iTwoP-jaU0rhhHgojxMdXCDhB8",
    "AIzaSyASgxIkI7xM2QwcNBOUWT7sDqpBRKAOvYs"
]

class AnalysisRequest(BaseModel):
    text: str
    task: str = "dashboard"

@app.get("/")
async def root():
    return {"status": "running", "message": "API is online."}

@app.get("/health")
async def health_check():
    return {"status": "healthy"}

def get_best_free_model_and_key():
    for api_key in API_KEYS:
        list_url = f"https://generativelanguage.googleapis.com/v1beta/models?key={api_key}"
        try:
            resp = requests.get(list_url, timeout=10)
            if resp.status_code == 200:
                data = resp.json()
                available_models = data.get("models", [])
                
                flash_models = []
                other_models = []
                
                for model in available_models:
                    if "generateContent" in model.get("supportedGenerationMethods", []):
                        name = model["name"].replace("models/", "")
                        if "flash" in name.lower():
                            flash_models.append(name)
                        else:
                            other_models.append(name)
                            
                if flash_models:
                    return flash_models[0], api_key
                elif other_models:
                    return other_models[0], api_key
        except Exception as e:
            print(f"Error fetching models for key {api_key[-4:]}: {e}")
            pass
    return None, None

@app.post("/dashboard")
async def generate_dashboard(request: AnalysisRequest):
    start_time = time.time()
    input_data = request.text[:15000] 
    
    model_name, working_key = get_best_free_model_and_key()
    
    if not model_name:
        raise HTTPException(status_code=500, detail="No free models available for your API keys.")
        
    print(f"Using model: {model_name} with key ending in ...{working_key[-4:]}")
    
    prompt = f"""
    You are a strict, mathematically precise Data Analyst AI. 
    Your job is to analyze the provided raw data and calculate exact metrics.
    
    CRITICAL INSTRUCTIONS:
    1. You MUST actually read the data, count the rows, and sum the numbers. 
    2. DO NOT hallucinate, guess, or use placeholder numbers. 
    3. If you cannot calculate a specific number, do not include it.
    4. All "value" fields in charts and metrics MUST be real numbers calculated directly from the data provided.
    
    The raw data to analyze is:
    ---
    {input_data}
    ---
    
    You MUST return ONLY a valid JSON object with this exact structure. 
    Replace all <placeholders> with actual calculated values from the data.
    
    {{
      "summary": "A factual summary of exactly what the data is and the most obvious mathematical trend.",
      "metrics": [
        {{"label": "<Exact Metric Name 1>", "value": "<Exact Calculated Value>", "change": "<Exact Change if available, else N/A>"}},
        {{"label": "<Exact Metric Name 2>", "value": "<Exact Calculated Value>", "change": "<Exact Change if available, else N/A>"}},
        {{"label": "<Exact Metric Name 3>", "value": "<Exact Calculated Value>", "change": "<Exact Change if available, else N/A>"}},
        {{"label": "<Exact Metric Name 4>", "value": "<Exact Calculated Value>", "change": "<Exact Change if available, else N/A>"}}
      ],
      "charts": [
        {{
          "type": "bar",
          "title": "<Title based on data categories>",
          "data": [{{"name": "<Category 1>", "value": <Exact Sum or Count>}}, {{"name": "<Category 2>", "value": <Exact Sum or Count>}}]
        }},
        {{
          "type": "pie",
          "title": "<Title based on data distribution>",
          "data": [{{"name": "<Segment 1>", "value": <Exact Count>}}, {{"name": "<Segment 2>", "value": <Exact Count>}}]
        }}
      ],
      "insights": ["Factual finding 1 based on the numbers", "Factual finding 2 based on the numbers"],
      "recommendations": ["Actionable recommendation based strictly on the data"],
      "risks": ["Risk identified in the data"],
      "opportunities": ["Opportunity identified in the data"],
      "tables": [
        {{
          "title": "Data Sample Breakdown",
          "headers": ["<Col 1>", "<Col 2>", "<Col 3>"],
          "rows": [
            ["<Actual Row 1 Data>", "<Actual Row 1 Data>", "<Actual Row 1 Data>"],
            ["<Actual Row 2 Data>", "<Actual Row 2 Data>", "<Actual Row 2 Data>"]
          ]
        }}
      ],
      "confidence_score": 90,
      "dataset_info": {{
        "name": "<Inferred Dataset Name>",
        "records": <Exact count of rows/records in the data>, 
        "columns": <Exact count of columns/fields in the data>, 
        "detected_type": "<Type of data>", 
        "upload_time": "0.1s", 
        "processing_time": "1.2s"
      }}
    }}
    """
    
    payload = {
        "contents": [{"parts": [{"text": prompt}]}],
        "generationConfig": {
            "temperature": 0.1,
            "maxOutputTokens": 8000
        }
    }

    url = f"https://generativelanguage.googleapis.com/v1beta/models/{model_name}:generateContent?key={working_key}"
    
    try:
        response = requests.post(url, json=payload, headers={"Content-Type": "application/json"}, timeout=45)
        
        if response.status_code != 200:
            error_msg = f"Gemini API Error {response.status_code}: {response.text}"
            print(error_msg)
            raise HTTPException(status_code=500, detail=error_msg)
            
        gemini_result = response.json()

        if 'candidates' in gemini_result and len(gemini_result['candidates']) > 0:
            if 'content' in gemini_result['candidates'][0] and 'parts' in gemini_result['candidates'][0]['content']:
                raw_text = gemini_result['candidates'][0]['content']['parts'][0]['text']
            else:
                error_msg = f"Gemini blocked the prompt. Full response: {json.dumps(gemini_result)}"
                print(error_msg)
                raise HTTPException(status_code=500, detail=error_msg)
        else:
            error_msg = f"Gemini returned no candidates. Full response: {json.dumps(gemini_result)}"
            print(error_msg)
            raise HTTPException(status_code=500, detail=error_msg)
        
        if raw_text.startswith("```json"):
            raw_text = raw_text[7:]
        if raw_text.endswith("```"):
            raw_text = raw_text[:-3]
            
        # --- ADDED JSON PARSE ERROR HANDLING ---
        try:
            dashboard_data = json.loads(raw_text.strip())
        except json.JSONDecodeError:
            print(f"JSON Parse Error. Raw text from AI: {raw_text}")
            raise HTTPException(status_code=500, detail="The AI returned an empty or invalid JSON response. Please try again.")
        
        processing_time = f"{time.time() - start_time:.1f}s"
        if 'dataset_info' in dashboard_data:
            dashboard_data['dataset_info']['processing_time'] = processing_time
        else:
            dashboard_data['dataset_info'] = {"processing_time": processing_time}
        
        return dashboard_data
        
    except HTTPException:
        raise
    except Exception as e:
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=f"Internal Server Error: {str(e)}")