Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
b3b288c
Fixed bug where path to index.html is in wrong place
marikmartinez Jun 22, 2026
9a86e69
Created data_attributes script for data profile class
marikmartinez Jun 26, 2026
869a8e5
Created function that generates data profile dataframe
marikmartinez Jun 29, 2026
13258ec
Modified update_errors_table so it returns the error_df so it can be …
marikmartinez Jun 29, 2026
3a35c5c
Modified update_errors_table so it returns the error_df so it can be …
marikmartinez Jun 29, 2026
ac33f76
Created update_data_profile_table
marikmartinez Jun 29, 2026
dd861b4
Imported create_data_profile_df into wrangler_routes_sql.py
marikmartinez Jun 29, 2026
ebac2a9
Updated create_previews_1d and create_previews_2d so they both update…
marikmartinez Jun 29, 2026
3f31ee5
Added import for DataProfile
marikmartinez Jun 29, 2026
c1846dc
Created DataProfile class (still a WIP)
marikmartinez Jun 29, 2026
4388c4a
Switched functions from pandas df summary stats functions to (slightl…
marikmartinez Jul 5, 2026
0f7fa11
Removed pandas import
marikmartinez Jul 5, 2026
c04878e
Moved data_attributes.py from ai_utils to db_utils
marikmartinez Jul 5, 2026
8fc8090
Moved data_attributes.py from ai_utils to db_utils
marikmartinez Jul 5, 2026
812c5e5
Renamed data_attributes.py to data_profile.py
marikmartinez Jul 5, 2026
8bef2a9
Renamed data_attributes.py to data_profile.py
marikmartinez Jul 5, 2026
737c2ac
Added data profile table to the preview table functions
marikmartinez Jul 5, 2026
a3055a2
Added creation of data profile table into load_file()
marikmartinez Jul 5, 2026
adfcc71
Fixed bug in create_data_profile_df
marikmartinez Jul 5, 2026
c2f47a6
Added loading of dp table into _clone_table_pair
marikmartinez Jul 5, 2026
2c3f9ce
Refactored create_previews args 'preview_name_fn' to 'safe_pg_name_fn'
marikmartinez Jul 5, 2026
f4ea61a
Refactored variable name preview_delete and preview_impute to preview…
marikmartinez Jul 5, 2026
db4ed96
Added params argument to fetch_sql
marikmartinez Jul 5, 2026
5312c77
Optimized updating of errors table and data profile table. WIP: still…
marikmartinez Jul 5, 2026
b95e8f3
Import text from sqlalchemy
marikmartinez Jul 5, 2026
93cc0ba
Added documentation to data_profile.py
marikmartinez Jul 5, 2026
a949d20
Added documentation to data_profile.py
marikmartinez Jul 5, 2026
5281a2b
Added documentation in service_helpers for create_data_profile_df()
marikmartinez Jul 5, 2026
9f8dee7
Moved ColumnTypes class into data_profile.py and added column types f…
marikmartinez Jul 10, 2026
44c1cf7
Removed ColumnTypes from db_functions_sql.py and added data profile i…
marikmartinez Jul 10, 2026
aa2af9c
Removed pandas dataframe alternate in data profile calculations (incl…
marikmartinez Jul 10, 2026
62f84e4
Added main_df argument to build_attribute_distributions
marikmartinez Jul 10, 2026
b0e2ac1
Cleaned up data_profile.py and fixed SQL queries not working for _cal…
marikmartinez Jul 10, 2026
380e97a
Refactored run_detectors to create_error_df
marikmartinez Jul 10, 2026
3797e0b
Fixed bug relating to dtypes not matching up when trying to update ta…
marikmartinez Jul 11, 2026
5f050a5
Cleaned up create_error_df and create_data_profile_df
marikmartinez Jul 11, 2026
47ec3e9
Changed argument name of execute_wrangle_preview to be more clear
marikmartinez Jul 11, 2026
317abca
Cleaned up update_data_profile_table
marikmartinez Jul 11, 2026
d21cf8d
Cleaned up wrangle_delete_column
marikmartinez Jul 11, 2026
3daa100
Created get_table_dtypes function to fix error with update_table wher…
marikmartinez Jul 17, 2026
9f5b591
Changed ColumnTypes so numeric categories are columns with majority n…
marikmartinez Jul 17, 2026
ec9a06d
Made it so numeric summary stats work even with mixed numeric columns…
marikmartinez Jul 17, 2026
a150433
Fixed bug in _calculate_error_count_dict
marikmartinez Jul 17, 2026
6227fa9
Added functions to get list different column types
marikmartinez Jul 17, 2026
099cac4
Refactored categorical_mixed to mixed_cols
marikmartinez Jul 17, 2026
6b9682c
Created tests for Data Profile functions
marikmartinez Jul 17, 2026
e43b827
Moved ColumnTypes class to its own script
marikmartinez Jul 19, 2026
a96f717
Moved ColumnTypes class to its own script
marikmartinez Jul 19, 2026
2ec0c8b
Revert "Removed ColumnTypes from db_functions_sql.py and added data p…
marikmartinez Jul 19, 2026
d2862af
Reapply "Removed ColumnTypes from db_functions_sql.py and added data …
marikmartinez Jul 19, 2026
af51b4d
Revert "Added functions to get list different column types"
marikmartinez Jul 19, 2026
d91e8f7
added pure numeric and pure categorical columns to column_types.py
marikmartinez Jul 19, 2026
bbc3411
Revert "Moved ColumnTypes class into data_profile.py and added column…
marikmartinez Jul 19, 2026
78c572f
Revert "Removed ColumnTypes from db_functions_sql.py and added data p…
marikmartinez Jul 19, 2026
bfdfc38
Reverted back to the old column_types function implementation. A fix …
marikmartinez Jul 20, 2026
5b95618
Fixed some bugs that I missed earlier
marikmartinez Jul 20, 2026
7a6eb4b
Modified _calculate_category_count_dict to not make the dict if all r…
marikmartinez Jul 24, 2026
f6cb9ce
Fixed bug where binned_data being None was causing an error.
marikmartinez Jul 24, 2026
a45e269
Merged in changes from dreu-minor-bug-fixes
marikmartinez Jul 24, 2026
48dd184
Added debugging print statements
marikmartinez Jul 24, 2026
8b57dcd
Added base_table_name variable to DBOperations. Refactored generate_t…
marikmartinez Jul 24, 2026
359c4c0
Implemented logger_utils.py (initialize_user_log and update_user_log …
marikmartinez Jul 24, 2026
5abbb74
Implemented logger_utils.py (initialize_user_log and update_user_log …
marikmartinez Jul 24, 2026
1c2a2b8
Added calls to initialize and update action logs (update when dataset…
marikmartinez Jul 24, 2026
16b9ab9
WIP: Adding update_action_log to deleting column action
marikmartinez Jul 24, 2026
30e181f
Created logger utils functions
marikmartinez Jul 31, 2026
6c29668
Added logging functions to API calls and moved some logging function …
marikmartinez Jul 31, 2026
a05a873
Created ablation_study.py
marikmartinez Aug 7, 2026
89b8202
Added logger to app and changed print statements in except statements…
marikmartinez Aug 7, 2026
53fbac8
Implemented copy_table_to_csv function in execute_sql.py
marikmartinez Aug 7, 2026
2a45fe8
Fixed logic bug relating to calculating column stats in service_helpe…
marikmartinez Aug 10, 2026
9f98892
Added params argument to execute_sql function
marikmartinez Aug 10, 2026
93d120e
Implemented create_empty_settings_df function
marikmartinez Aug 10, 2026
73c3c58
Fixed action_duration related bugs & cleaned up code
marikmartinez Aug 10, 2026
02e944c
Fixed if statement in data_profile.py
marikmartinez Aug 10, 2026
89d41b1
Added try except block in data_profile.py (_calculate_class_error_cou…
marikmartinez Aug 10, 2026
a018c3c
Created ai_routes.py
marikmartinez Aug 10, 2026
c203271
Created ai_utils.py
marikmartinez Aug 10, 2026
7fc83ab
Separated execute_wrangle logic from execute_wrangle api call
marikmartinez Aug 10, 2026
23f9563
Created __init__.py for ablation study directory
marikmartinez Aug 10, 2026
7a29779
Fixed ablation study config dict
marikmartinez Aug 10, 2026
9d7c6c8
Added todo for later
marikmartinez Aug 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def format(self, record):
_werkzeug_handler = logging.StreamHandler()
_werkzeug_handler.setFormatter(_WhiteFormatter('%(message)s'))
logging.getLogger('werkzeug').handlers = [_werkzeug_handler]
logger = logging.getLogger('werkzeug')

from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT

Expand Down Expand Up @@ -107,8 +108,6 @@ def load_database_info():
# Create the database if it does not exist
create_database_if_not_exists(connection, db_name)



"""
then we use SQLAlchemy (create_engine) for everything else
this is the engine that gets passed around to fetch_sql, execute_sql,
Expand Down
Empty file added app/ablation_study/__init__.py
Empty file.
127 changes: 127 additions & 0 deletions app/ablation_study/ablation_study.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import os

from dotenv import load_dotenv
import datetime


from app import app as app_module
from app import db_operations, engine

import json

from app.db_utils.ai_utils import parse_json_response
from app.server_utils.logger_utils import initialize_action_log

datasets_paths = ['provided_datasets/mari_dataset.csv']
#models = [{"model": "qwen/qwen3.6-27b", "provider": "groq"}]
models = [{"model": "openai/gpt-oss-20b", "provider": "groq"}]

def variant(name, **overrides):
baseline = {
"name": "baseline",
"include_error_log": True,
"include_data_profile": True,

"include_action_log": True,
"action_log_limit": 10,
"include_full_dataset": True
}

# gets the baseline and overrides the key(s) in the overrides variable
config = {**baseline, **overrides}
config["name"] = name
return config


def is_stop_action(action_name):
if action_name == "stop":
return True
else:
return False


if __name__ == "__main__":
load_dotenv()

# TODO: double check this
ablation_configs = [
variant("baseline"),
variant("no_error_log", include_error_log=False),
variant("no_data_profile", include_data_profile=False),
variant("no_action_log", include_action_log=False),
variant("no_full_dataset", include_full_dataset=False),
variant("no_action_log_limit", action_log_limit=None) # Includes full action log
]

client = app_module.test_client()
app_module.testing = False

results = []

for model_dict in models:

model = model_dict["model"]
model_provider = model_dict["provider"]

update_settings_table_result = client.post('/api/ai_helper/update_settings_table', json={"model_name": model, "provider": model_provider})
data = update_settings_table_result.get_json()
assert data["success"] == True

for config in ablation_configs:
# reset globals living in the app package namespace
app_module.wrangle_occurred = False

# reset attributes on the Flask object itself
app_module.pgraph_for_session = None

# reset your stateful class instance
db_operations.reset()
for dataset in datasets_paths:
print(f"Running config {config} wth model {model} and dataset {dataset}")
initialize_action_log(engine, reset_log=True)

with open(dataset, 'rb') as f:
upload_result = client.post('/api/upload', data={'file': (f, dataset)},
content_type='multipart/form-data')
data = upload_result.get_json()
assert data["success"] == True

result_dict = {} # Dict added to the result json
result_dict["model"] = model_dict
result_dict["config_name"] = config["name"]
result_dict["dataset"] = dataset
result_dict["actions"] = []

action_plan_batch = 0

stop_action_found = False

while not stop_action_found:
action_plan_batch += 1

actions = None

# Get actions
get_action_plan_result = client.post('/api/ai_helper/get_action_plan')
action_plan_result_json = get_action_plan_result.get_json()
assert action_plan_result_json["success"] == True
action_plan_json = action_plan_result_json["json_action_plan"]

# Go through all actions and perform each of them
for action_dict in action_plan_json:

if is_stop_action(action_dict["action_name"]):
stop_action_found = True

action_result = client.post('/api/ai_helper/perform_llm_action', json=action_dict)
action_dict["action_plan_batch"] = action_plan_batch
action_dict["timestamp"] = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
action_dict["success"] = action_result.get_json()

results.append(action_dict)


with open(f'ablation_results_{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.json', 'w') as outfile:
json.dump(results, outfile)


97 changes: 97 additions & 0 deletions app/db_utils/ai_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import os
from app.db_utils.execute_sql import copy_table_to_csv
from app import logger
import json
import ast

import random
import time


# Overwriting the csv every time the LLM needs it to be updated; we don't really need to save the old ones

def update_csvs_for_llm(error_table_name, data_profile_name, action_log_name, full_dataset_name):
print(f"UPDATE CSVS FOR LLM PATHS error_table_name: {error_table_name}, data_profile_name: {data_profile_name} action_log_name: {action_log_name}")

action_log_csv_path = "action_log.csv"
error_log_csv_path = "error_log.csv"
data_profile_csv_path = "data_profile.csv"
full_dataset_csv_path = "full_dataset.csv"

_THIS_DIR = os.path.dirname(os.path.abspath(__file__))
FILES_FOR_LLM_PATH = os.path.abspath(os.path.join(_THIS_DIR, '..', 'files_for_llm'))

action_log_csv_path = FILES_FOR_LLM_PATH + '/' + f'{action_log_csv_path}'
error_log_csv_path = FILES_FOR_LLM_PATH + '/' + f'{error_log_csv_path}'
data_profile_csv_path = FILES_FOR_LLM_PATH + '/' + f'{data_profile_csv_path}'
full_dataset_csv_path = FILES_FOR_LLM_PATH + '/' + f'{full_dataset_csv_path}'


table_name_tuple_list = [(action_log_name, action_log_csv_path),
(error_table_name, error_log_csv_path),
(data_profile_name, data_profile_csv_path),
(full_dataset_name, full_dataset_csv_path)]

write_tables_to_csv(table_name_tuple_list)

return (error_log_csv_path, data_profile_csv_path, action_log_csv_path, full_dataset_csv_path)



def write_tables_to_csv(table_name_tuple_list):
from app import engine
for (table_name, csv_path) in table_name_tuple_list:

# Clear the existing CSV file if it exists
if os.path.exists(csv_path):
os.remove(csv_path)

copy_table_to_csv(table_name, csv_path, engine)


def parse_json_response(llm_json_response):
try:
return json.loads(llm_json_response)
except json.JSONDecodeError:
try:
return ast.literal_eval(llm_json_response)
except (ValueError, SyntaxError) as e:
logger.exception(f"Could not parse response as dict or JSON")


def call_with_retry(function, func_args, max_tries=5):
for attempt in range(max_tries):
try:
result = function(*func_args)

return result
except Exception:
logger.exception("Error occurred while calling LLM function")
if attempt == max_tries - 1:
raise

# TODO: is this okay
delay = 10
delay *= random.uniform(0.5, 1.5) # jitter
time.sleep(delay)



def get_api_key(provider):
key_map = {
"openai": "OPENAI_API_KEY",
"anthropic": "ANTHROPIC_API_KEY",
"groq": "GROQ_API_KEY"
}

key = os.environ.get(key_map[provider])
if key is None:
raise ValueError(f"Could not find API key for {provider}")
return key







Loading