from fastapi import APIRouter, HTTPException from pydantic import BaseModel from swiggy_scraper import fetch_swiggy_orders from datetime import datetime from openai import OpenAI import os, json router = APIRouter() client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) class QueryInput(BaseModel): query: str class DateRange(BaseModel): start_date: str end_date: str # @router.post("/parse_query") # def parse_query_llm(input: QueryInput): # print("\ninput query:") # print(input) # today_str = datetime.today().strftime("%d-%b-%Y") # system = ( # f"You are a date range extractor.\n" # f"Today is {today_str}.\n" # "Extract start_date and end_date in 'DD-MMM-YYYY' format.\n" # "Respond with:\n" # "Output ONLY a valid JSON object like:\n" # '{ "start_date": "17-May-2025", "end_date": "18-May-2025" }\n' # 'no extra commentry needed.' # ) # try: # rsp = client.chat.completions.create( # model="gpt-4o-mini", # temperature=0, # messages=[ # {"role": "system", "content": system}, # {"role": "user", "content": input.query}, # ] # ) # result = json.loads(rsp.choices[0].message.content.strip()) # if "start_date" not in result or "end_date" not in result: # raise ValueError("Invalid response format") # print("results:", result) # return result # except Exception as e: # raise HTTPException(status_code=400, detail=str(e)) def _llm(messages, model="gpt-4o-mini", temperature=0): rsp = client.chat.completions.create( model=model, temperature=temperature, messages=messages, ) return rsp.choices[0].message.content.strip() # ---------- Stage 1: classify + extract dates -------------------------- def _extract_scope(user_query: str): today_str = datetime.today().strftime("%d-%b-%Y") sys_prompt = f""" Today is {today_str}. You are a SCOPING assistant: decide if the user's text is about Swiggy food orders, extract ONE date range, and keep the leftover words. Return ONLY valid JSON like: {{ "is_swiggy_query": true, "start_date": "15-May-2025", "end_date": "20-May-2025", "remainder": "non veg expense" }} Rules: • Accept natural phrases (“last week”, “since 1 May”). • If no dates → start_date & end_date = null. • If not Swiggy related → is_swiggy_query=false and remainder is full original text. • Do NOT invent a remainder; it is literally whatever words follow the date phrase(s). """ raw = _llm( [ {"role": "system", "content": sys_prompt}, {"role": "user", "content": user_query} ] ) return json.loads(raw) # ---------- Stage 2: shrink “remainder” into an intent ----------------- def _extract_intent(remainder: str): sys_prompt = """ You are an INTENT classifier for Swiggy-order analytics. Map the sentence into one concise snake_case intent. Allowed intents (extendable): • calculate_expense • list_orders • list_items • list_nonveg_items • list_veg_items • count_orders • unknown Return JSON: { "intent": "calculate_expense" } If unsure choose "unknown". """ raw = _llm( [ {"role": "system", "content": sys_prompt}, {"role": "user", "content": remainder.strip()} ] ) return json.loads(raw)["intent"] # ---------- FastAPI route ---------------------------------------------- @router.post("/parse_query") def parse_query_llm(input: QueryInput): try: scope = _extract_scope(input.query) print("scope") print(scope) # If it is a Swiggy query, classify intent; else, intent = "unrelated" if scope.get("is_swiggy_query", False): intent = _extract_intent(scope.get("remainder", "")) else: intent = "unrelated" result = { "is_swiggy_query": scope["is_swiggy_query"], "start_date": scope["start_date"], "end_date": scope["end_date"], "intent": intent } print("result") print(result) return result except Exception as e: raise HTTPException(status_code=400, detail=str(e)) @router.post("/get_orders") def get_orders(range: DateRange): try: orders = fetch_swiggy_orders(range.start_date, range.end_date) return orders except Exception as e: raise HTTPException(status_code=500, detail=str(e))