Spaces:
Sleeping
Sleeping
| import os | |
| import pandas as pd | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from langchain_openai import ChatOpenAI | |
| from langchain_experimental.agents import create_pandas_dataframe_agent | |
| app = FastAPI(title="BiteSight Analytics API") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # In production, replace with your Vercel URL | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Load data on startup | |
| DATA_FILE = "data_client.csv" | |
| try: | |
| df = pd.read_csv(DATA_FILE) | |
| print(f"Successfully loaded {DATA_FILE} with {len(df)} rows.") | |
| except Exception as e: | |
| print(f"Warning: Could not load {DATA_FILE}: {e}") | |
| df = pd.DataFrame() # Fallback empty dataframe | |
| class ChatRequest(BaseModel): | |
| question: str | |
| async def chat(request: ChatRequest): | |
| try: | |
| if df.empty: | |
| return {"answer": "Error: Data source is unavailable on the server."} | |
| akashml_api_key = os.environ.get("AKASHML_API_KEY") | |
| if not akashml_api_key: | |
| return {"answer": "Error: AKASHML_API_KEY environment variable tidak diset di Hugging Face Space Anda."} | |
| # Initialize AkashML LLM via ChatOpenAI (OpenAI-compatible) | |
| llm = ChatOpenAI( | |
| openai_api_key=akashml_api_key, | |
| openai_api_base="https://api.akashml.com/v1", | |
| model_name="MiniMaxAI/MiniMax-M2.5", | |
| temperature=0.2, | |
| max_tokens=8192 | |
| ) | |
| system_message = ( | |
| "You are a world-class F&B Executive Business Consultant and Data Scientist " | |
| "with 20+ years of experience advising top restaurant chains and retail F&B brands. " | |
| "You have deep expertise in sales analytics, consumer behavior, operational efficiency, and growth strategy.\n\n" | |
| "RESPONSE STYLE:\n" | |
| "- If the user sends a general greeting, respond warmly and naturally in Indonesian.\n" | |
| "- If the user asks a business/data question, give a THOROUGH, INSIGHTFUL, and EXECUTIVE-LEVEL analysis. " | |
| "Do NOT give shallow or overly brief answers. Go deep. Uncover layers of insight.\n" | |
| "- ALWAYS include: key data facts WITH specific numbers, analytical commentary explaining the 'why', " | |
| "patterns/anomalies/trends, business implications, AND concrete actionable recommendations.\n" | |
| "- Structure your response FREELY using rich Markdown: use headers (##, ###), bold text, " | |
| "bullet lists, and comparison tables when helpful. DO NOT use any rigid fixed template.\n" | |
| "- ALWAYS respond in fluent, professional INDONESIAN.\n\n" | |
| "PANDAS DATA ENGINE RULES (CRITICAL):\n" | |
| "- A pandas DataFrame named `df` with ALL rows of client transaction data is ALREADY LOADED.\n" | |
| "- ALWAYS run actual pandas operations to get real numbers. NEVER estimate or fabricate data.\n" | |
| "- DO NOT recreate the dataframe using StringIO or hardcoded text. Use `df` directly.\n" | |
| "- Run MULTIPLE pandas queries if needed to build a complete picture " | |
| "(e.g., value_counts, groupby, mean, corr, describe, resample, etc.).\n" | |
| "- Cross-reference multiple dimensions when relevant (e.g., payment method vs. branch vs. time of day).\n\n" | |
| "DATA PRESENTATION RULES:\n" | |
| "- ALWAYS state specific numbers, percentages, and timeframes. Vague statements are not acceptable.\n" | |
| "- Currency is US Dollars ($). ALWAYS format as $XX.XX. NEVER use 'Rp' or 'Rupiah'.\n" | |
| "- Do NOT explain the dataset schema or mention row counts unless explicitly asked.\n" | |
| "- When comparing metrics, provide both absolute numbers AND percentages for clarity.\n" | |
| ) | |
| agent = create_pandas_dataframe_agent( | |
| llm, | |
| df, | |
| agent_type="tool-calling", | |
| allow_dangerous_code=True, | |
| handle_parsing_errors=True, | |
| verbose=True, | |
| prefix=system_message | |
| ) | |
| response = agent.invoke({"input": request.question}) | |
| return {"answer": response.get("output", "No output generated.")} | |
| except Exception as e: | |
| error_msg = str(e) | |
| if "Could not parse" in error_msg: | |
| extracted = error_msg.split("Could not parse")[-1].replace("LLM output:", "").strip(" `'\"") | |
| return {"answer": extracted} | |
| return {"answer": f"Terjadi kendala saat memproses instruksi. Jika Anda sekadar menyapa, halo juga! Jika ini pertanyaan data, format Model mungkin tidak sesuai.\n\n**Log Server:** `{error_msg}`"} | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) # Hugging Face Spaces typical port | |