File size: 6,721 Bytes
8437d61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
from pydantic import BaseModel
import uvicorn
from langgraph.graph import START, StateGraph
from Cleaner_Agent import DataAnalystAgent, AgentStateModel
from fastapi import FastAPI, UploadFile, File, HTTPException
import pandas as pd
import tempfile

from Report_agent import Report_agent 

import uuid
from fastapi.staticfiles import StaticFiles
from Visualizer_agent import Visualizer_agent 

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

app = FastAPI()
agent = DataAnalystAgent()

PLOTS_DIR = "generated_plots"
os.makedirs(PLOTS_DIR, exist_ok=True)
app.mount("/generated_plots", StaticFiles(directory=PLOTS_DIR), name="generated_plots")

class CleanRequest(BaseModel):
    path: str
    instructions: str | None = None

class CleanResponse(BaseModel):
    status: str
    message: str
    cleaned_csv_content: str | None = None

@app.post("/clean-data", response_model=CleanResponse)
async def clean_data_endpoint(request: CleanRequest):
    try:
        print(f"Received request to clean data at path: {request.path}")

        # --- Your LangGraph Logic ---
        initial_state = AgentStateModel(
            Instructions=request.instructions,
            Path=request.path,
            messages=[], Analysis=[], next="", current_reasoning=""
        )
        graph = StateGraph(AgentStateModel)
        graph.add_node("supervisor", agent.supervisor_node)
        graph.add_node("PreprocessingPlanner_node", agent.PreprocessingPlanner_node)
        graph.add_node("Cleaner_node", agent.Cleaner_node)
        graph.add_edge(START, "supervisor")
        compiled_graph = graph.compile()
        final_state = compiled_graph.invoke(initial_state)
        # --- End of Your Logic ---

        output_filename = "cleaned_" + os.path.basename(request.path)
        output_filepath = os.path.join(os.path.dirname(request.path), output_filename)
        
        if not os.path.exists(output_filepath):
             raise FileNotFoundError(f"Cleaner did not produce the expected output file: {output_filepath}")

        with open(output_filepath, 'r', encoding='utf-8') as f:
            csv_content = f.read()
        
        print("Successfully processed data and read cleaned file.")

        return {
            "status": "success", 
            "message": "Data cleaning process completed.",
            "cleaned_csv_content": csv_content
        }

    except Exception as e:
        print(f"An error occurred: {e}")
        raise HTTPException(status_code=500, detail=str(e))
    
# --- REPORT GENERATION ENDPOINT ---
class ReportRequest(BaseModel):
    path: str
    instructions: str | None = None   # optional prompt addon


class ReportResponse(BaseModel):
    success: bool
    parsed_report: dict | None = None
    raw_output: str | None = None
    error: str | None = None




@app.post("/generate-report", response_model=ReportResponse)
async def generate_report_endpoint(request: ReportRequest):
    """

    Endpoint that triggers the Report Agent to generate a structured business report.

    Expects:

      - path: str -> path to CSV file

      - instructions: Optional custom instructions

    """
    try:
        print(f"Received request to generate business report from: {request.path}")

        # Call Reporter Agent
        result = Report_agent(request.path)

        if result.get("success"):
            return {
                "success": True,
                "parsed_report": result.get("parsed_report"),
                "raw_output": result.get("raw_output"),
                "error": None,
            }
        else:
            return {
                "success": False,
                "parsed_report": None,
                "raw_output": result.get("output"),
                "error": result.get("error"),
            }

    except Exception as e:
        print(f"Report generation error: {e}")
        return {
            "success": False,
            "parsed_report": None,
            "raw_output": None,
            "error": str(e),
        }

class VisualizeRequest(BaseModel):
    path: str

class VisualizeResponse(BaseModel):
    success: bool
    parsed_visuals: dict | None = None
    raw_output: str | None = None
    error: str | None = None

# --- 4. The Endpoint ---
@app.post("/generate-visualizations", response_model=VisualizeResponse)
async def generate_visualizations_endpoint(request: VisualizeRequest):
    """

    Endpoint that triggers the Visualizer Agent to generate charts.

    Images are saved locally and returned as accessible URLs.

    """
    try:
        print(f"Received request to visualize data from: {request.path}")

        # 1. Create a unique sub-directory for this specific run to avoid file conflicts
        # Example: generated_plots/550e8400-e29b-41d4-a716-446655440000/
        run_id = str(uuid.uuid4())
        output_dir = os.path.join(PLOTS_DIR, run_id)
        os.makedirs(output_dir, exist_ok=True)

        # 2. Run the Visualizer Agent
        # We pass the absolute path for 'output_dir' so Python knows where to write
        abs_output_dir = os.path.abspath(output_dir)
        
        result = Visualizer_agent(df_path=request.path, output_dir=abs_output_dir)

        # 3. Process the result to convert local file paths to HTTP URLs
        # The agent returns absolute paths (e.g., D:/Neon/generated_plots/uuid/plot.png)
        # We need to send back URLs (e.g., http://localhost:8000/generated_plots/uuid/plot.png)
        
        if result.get("success") and result.get("parsed_visuals"):
            base_url = "http://localhost:8000/generated_plots" # Update if deployed elsewhere
            
            visuals = result["parsed_visuals"].get("visualizations", [])
            for vis in visuals:
                # Extract filename from the full path
                filename = os.path.basename(vis["file_path"])
                # Construct the serveable URL
                vis["file_path"] = f"{base_url}/{run_id}/{filename}"

        return {
            "success": result.get("success"),
            "parsed_visuals": result.get("parsed_visuals"),
            "raw_output": result.get("raw_output"),
            "error": result.get("error"),
        }

    except Exception as e:
        print(f"Visualization error: {e}")
        return {
            "success": False,
            "parsed_visuals": None,
            "raw_output": None,
            "error": str(e),
        }
    

# --- Standard `uvicorn.run` call (No changes) ---
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)