Skip to content

Commit 0a114da

Browse files
committed
merge frontend and updated LLM
1 parent 7301e13 commit 0a114da

6 files changed

Lines changed: 0 additions & 298 deletions

File tree

backend/LLM/router.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,5 @@
11
# llm/router.py
22
import json
3-
<<<<<<< HEAD
4-
from typing import Any, Dict
5-
6-
from .client import client, MODEL
7-
from ..models import Intent
8-
9-
# Describe allowable actions & fields (for the prompt)
10-
ACTION_ENUM = ["upload_data", "find_anomalies", "get_output", "rerun", "reset", "help"]
11-
12-
SYSTEM_PROMPT = f"""
13-
You are an intent parser for the ADaS app. Always return a single JSON object
14-
with keys "action" and "params". The "action" must be one of: {ACTION_ENUM}.
15-
The "params" is an object of optional keys such as:
16-
- top_n: integer
17-
- num_features: integer
18-
- time_range: string like "past_day" | "past_week" | "past_month"
19-
- target_ip: string ip address
20-
- explanation: "none" | "simple" | "verbose"
21-
- sort_by: "ip" | "time" | "quantity" | "score"
22-
- uid_column: string
23-
Never include commentary, code fences, or extra text—only valid JSON.
24-
Infer only what is stated or strongly implied by the user text.
25-
If uncertain about a field, omit it from "params" rather than guessing.
26-
"""
27-
28-
def _validate_to_intent(raw: Dict[str, Any]) -> Intent:
29-
"""
30-
Convert a raw dict (from model) into our Pydantic Intent model.
31-
This enforces allowed enum values and types.
32-
"""
33-
# Minimal normalization (optional): lowercase action if present
34-
if isinstance(raw, dict) and "action" in raw and isinstance(raw["action"], str):
35-
raw["action"] = raw["action"].strip().lower()
36-
37-
# Pydantic validation (raises if invalid)
38-
return Intent.model_validate(raw)
39-
40-
41-
def resolve_intent(user_text: str) -> Intent:
42-
"""
43-
Parse natural language into a structured {action, params} using Chat Completions JSON mode.
44-
Falls back to plain text -> JSON parse if JSON mode isn't available in the SDK.
45-
"""
46-
# Preferred path: JSON mode (chat.completions with response_format={"type":"json_object"})
47-
=======
483
from json import JSONDecodeError
494
from typing import Any, Dict
505
from datetime import datetime, timedelta, timezone
@@ -259,24 +214,13 @@ def resolve_intent(user_text: str) -> Intent:
259214
Parse natural language into {action, params} via OpenAI JSON mode,
260215
then normalize and validate.
261216
"""
262-
>>>>>>> frontend
263217
try:
264218
resp = client.chat.completions.create(
265219
model=MODEL,
266220
messages=[
267221
{"role": "system", "content": SYSTEM_PROMPT},
268222
{"role": "user", "content": user_text},
269223
],
270-
<<<<<<< HEAD
271-
response_format={"type": "json_object"}, # strict JSON response
272-
)
273-
content = resp.choices[0].message.content or "{}"
274-
data = json.loads(content)
275-
return _validate_to_intent(data)
276-
277-
except TypeError:
278-
# Some environments may not support response_format; fallback to plain text then json.loads
279-
=======
280224
response_format={"type": "json_object"},
281225
)
282226
content = resp.choices[0].message.content or "{}"
@@ -285,37 +229,21 @@ def resolve_intent(user_text: str) -> Intent:
285229

286230
except TypeError:
287231
# Fallback if response_format is not supported
288-
>>>>>>> frontend
289232
resp = client.chat.completions.create(
290233
model=MODEL,
291234
messages=[
292235
{"role": "system", "content": SYSTEM_PROMPT},
293236
{"role": "user", "content": user_text},
294-
<<<<<<< HEAD
295-
]
296-
)
297-
content = resp.choices[0].message.content or "{}"
298-
299-
# Try to locate JSON in the reply (in case the model added extra text)
300-
# Simple heuristic: find the first '{' and last '}'.
301-
=======
302237
],
303238
)
304239
content = resp.choices[0].message.content or "{}"
305-
>>>>>>> frontend
306240
start = content.find("{")
307241
end = content.rfind("}")
308242
if start != -1 and end != -1 and end > start:
309243
payload = content[start : end + 1]
310244
else:
311245
payload = "{}"
312-
<<<<<<< HEAD
313-
314-
data = json.loads(payload)
315-
return _validate_to_intent(data)
316-
=======
317246
data = json.loads(payload)
318247
return _postprocess_and_validate(data)
319-
>>>>>>> frontend
320248

321249

backend/api/interpret.py

Lines changed: 0 additions & 60 deletions
This file was deleted.

backend/backendInterface.py

Lines changed: 0 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
import numpy as np
77
from sklearn.decomposition import PCA
88
from sklearn.preprocessing import StandardScaler, LabelEncoder
9-
<<<<<<< HEAD
10-
from reinforcementLearning import run_rl
11-
=======
129
from backend.reinforcementLearning import run_rl
13-
>>>>>>> frontend
1410

1511

1612
backend_data = {"df": None,
@@ -58,8 +54,6 @@ def clean_data(df):
5854

5955
###### FEATURE SELECTION & FIND ANOMALIES #####
6056

61-
<<<<<<< HEAD
62-
=======
6357
#############TESTING PURPOSES ONLY START #############
6458

6559
def find_anomalies(query, uid, num_feat, start=None, end=None, source_ip=None):
@@ -94,96 +88,11 @@ def find_anomalies(query, uid, num_feat, start=None, end=None, source_ip=None):
9488
return mock_table
9589

9690
#############TESTING PURPOSES ONLY END #############
97-
>>>>>>> frontend
9891
'''
9992
Summary: Takes the user query, performs feature selection and RL. Updates all global data.
10093
Input:
10194
Output:
10295
'''
103-
<<<<<<< HEAD
104-
def find_anomalies(query, uid, num_feat):
105-
106-
## FEATURE SELECTION
107-
main_identifiers = [uid] # TODO: could later add option for additional headings to ignore, otherwise switch to just UID
108-
backend_data["uid"] = uid
109-
df = backend_data["df"]
110-
num_entries = df.shape[0]
111-
drop = []
112-
for i in df:
113-
if df[i].dtype == 'O' and i not in main_identifiers: # qualitative
114-
qual_to_quant(df, i)
115-
if df[i].nunique() > (num_entries / 2) or df[i].nunique() == 1: # if more than 1/2 of data points have a unique label OR all have same label, drop the column. can change this!
116-
drop.append(i)
117-
# print(drop)
118-
cleaned_df = df.drop(columns = drop)
119-
qual_to_quant(cleaned_df, main_identifiers[0])
120-
backend_data["df"] = cleaned_df
121-
# print("Final Columns:", str(cleaned_df.shape[1]))
122-
# print(cleaned_df.head())
123-
124-
pca, features = get_features(cleaned_df, num_feat, main_identifiers)
125-
backend_data["features"] = features
126-
127-
## REINFORCEMENT LEARNING
128-
anomalies, cluster_sizes, final_features = run_rl(backend_data) #TODO: others for output data
129-
backend_data["anomalies"] = anomalies
130-
return anomalies
131-
132-
'''Converts the qualitative column col in df to quantitative values'''
133-
def qual_to_quant(df, col):
134-
le = LabelEncoder()
135-
df[col] = le.fit_transform(df[col])
136-
137-
"Returns an array with the indexes of the top n values in arr"
138-
def get_top_n_idx(n, arr):
139-
arr = np.abs(arr)
140-
top = np.argpartition(arr, -n)[-n:]
141-
return top
142-
143-
'''
144-
Returns a numpy array of the selected features from data using PCA.
145-
146-
PCA: develops unspecified number of components to represent data
147-
Feature Selection: getting the top_n features that have the highest weighted
148-
importance across all components
149-
'''
150-
def get_features(data, top_n, main_identifiers):
151-
copy = data.copy(deep=True) # Make a copy of the original data to avoid modifying it
152-
feat_options = copy.drop(columns=main_identifiers).copy(deep=True) # Drop columns like unique IDs that shouldn't be scaled
153-
154-
# Standardize the data
155-
scaler = StandardScaler()
156-
scaler.fit(feat_options)
157-
scaled_data = scaler.transform(feat_options)
158-
159-
pca = PCA(n_components=0.95) # should represent at least 95% of overall trends in data
160-
pca.fit(scaled_data)
161-
162-
### USE ABSOLUTE VALUE OF LOADINGS ONLY FOR FEATURE IMPORTANCE
163-
loadings = np.abs(pca.components_) # Get importance for each feature in each component
164-
# feature_importance = np.sum(loadings, axis=0) # Sum the absolute loadings for each feature across all components
165-
166-
### USE WEIGHTED LOADINGS BY EXPLAINED VARIANCE FOR FEATURE IMPORTANCE
167-
weighted_loadings = np.abs(pca.components_) * pca.explained_variance_ratio_.reshape(-1, 1)
168-
feature_importance = np.sum(weighted_loadings, axis=0)
169-
170-
171-
# Get the indexes of the top n most important features based on summed importance
172-
top = get_top_n_idx(top_n, feature_importance)
173-
174-
# Get the feature names for the most important features
175-
most_important_names = feat_options.columns[top]
176-
177-
# Print the selected important features
178-
print("Most Important Features:", most_important_names.tolist())
179-
180-
# Return the unique top features
181-
unique_feats = np.unique(most_important_names)
182-
print("Unique Features:", unique_feats.tolist())
183-
184-
# Return the selected unique features
185-
return pca, unique_feats
186-
=======
18796
# def find_anomalies(query, uid, num_feat, start=None, end=None, source_ip=None):
18897

18998
# ## FEATURE SELECTION
@@ -266,7 +175,6 @@ def get_features(data, top_n, main_identifiers):
266175

267176
# # Return the selected unique features
268177
# return pca, unique_feats
269-
>>>>>>> frontend
270178

271179

272180
##### GET OUTPUT #####
@@ -277,9 +185,6 @@ def get_features(data, top_n, main_identifiers):
277185
Output:
278186
'''
279187
def get_output():
280-
<<<<<<< HEAD
281-
return("Returning results")
282-
=======
283188
######## JUST FOR TESTING PURPOSES ########
284189
return {
285190
"ok": True,
@@ -288,4 +193,3 @@ def get_output():
288193
"summary": "Returning test results"
289194
}
290195
}
291-
>>>>>>> frontend

backend/main.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
11
from fastapi import FastAPI, File, UploadFile, HTTPException
2-
<<<<<<< HEAD
3-
from backendInterface import add_data, find_anomalies, get_output
4-
from fastapi.middleware.cors import CORSMiddleware
5-
6-
# Mount routers
7-
from .api.intent import router as intent_router
8-
from .api.interpret import router as interpret_router
9-
10-
=======
112
from backend.backendInterface import add_data, find_anomalies, get_output
123
from fastapi.middleware.cors import CORSMiddleware
134

145
# Mount routers
156
from backend.api.intent import router as intent_router
16-
>>>>>>> frontend
177
app = FastAPI()
188

199
# Allow frontend to call API from a different origin/port
@@ -26,11 +16,7 @@
2616

2717
# LLM routes
2818
app.include_router(intent_router)
29-
<<<<<<< HEAD
30-
app.include_router(interpret_router)
31-
=======
3219
# app.include_router(interpret_router)
33-
>>>>>>> frontend
3420

3521
# Original routes
3622
@app.post("/add_data/")

0 commit comments

Comments
 (0)