palexis3 commited on
Commit
8a18fd2
·
1 Parent(s): b08ed5c

Add categorizer files that were not committed

Browse files
app/categorization/categorizer.py CHANGED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Standard library imports
2
+ import re
3
+ import ast
4
+ import json
5
+ import logging
6
+ from typing import Any, List, Tuple, Optional, Dict, Union
7
+
8
+ # Third-party library imports
9
+ import numpy as np
10
+ import pandas as pd
11
+ import asyncio
12
+ from rapidfuzz import process
13
+ from tenacity import retry, wait_random_exponential, stop_after_attempt
14
+ from pydantic import ValidationError
15
+
16
+ # Local application/library specific imports
17
+ from langchain.chat_models import ChatOpenAI
18
+ from langchain.chains import LLMChain
19
+ from langchain.output_parsers import PydanticOutputParser, OutputFixingParser
20
+ from langchain.prompts import PromptTemplate
21
+ import template as template
22
+ from config import CATEGORY_REFERENCE_OUTPUT_FILE, TX_PER_LLM_RUN
23
+
24
+
25
+ def fuzzy_match_list_categorizer(
26
+ description: str,
27
+ descriptions: np.ndarray,
28
+ description_category_pairs: pd.DataFrame,
29
+ threshold: int = 75,
30
+ ) -> Optional[str]:
31
+ """Find the most similar transaction description and return its category.
32
+
33
+ This function uses fuzzy string matching to compare the input description
34
+ against a list of known descriptions. If a sufficient match is found,
35
+ the function returns the category associated with the matched description.
36
+
37
+ Args:
38
+ description (str): The transaction description to categorize.
39
+ descriptions (np.ndarray): Known descriptions to compare against.
40
+ description_category_pairs (pd.DataFrame): DataFrame mapping descriptions to categories.
41
+ threshold (int): Minimum similarity score to consider a match.
42
+
43
+ Returns:
44
+ str or None: Category of the matched description, or None if no match found.
45
+ """
46
+
47
+ # Fuzzy-match this description against the reference descriptions
48
+ match_results = process.extractOne(description, descriptions, score_cutoff=threshold)
49
+
50
+ # If a match is found, return the category of the matched description
51
+ if match_results:
52
+ return description_category_pairs.at[match_results[2], 'category']
53
+
54
+ return None
55
+
56
+
57
+ async def llm_list_categorizer(tx_list: pd.DataFrame) -> pd.DataFrame:
58
+ """Categorize a list of transactions using a language model.
59
+
60
+ This function uses a Language Model (LLM) to categorize a list of transaction descriptions.
61
+ It splits the input DataFrame into chunks and processes each chunk asynchronously to improve performance.
62
+
63
+ Args:
64
+ tx_list (pd.DataFrame): DataFrame containing the transaction descriptions to categorize.
65
+
66
+ Returns:
67
+ pd.DataFrame: DataFrame mapping transaction descriptions to their inferred categories.
68
+ """
69
+
70
+ # Initialize language model and prompt
71
+ llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125")
72
+ prompt = PromptTemplate.from_template(template=template.CATEGORY_TEMPLATE)
73
+ chain = LLMChain(llm=llm, prompt=prompt)
74
+
75
+ # Iterate over the DataFrame in batches of TX_PER_LLM_RUN transactions
76
+ tasks = [llm_sublist_categorizer(tx_list.attrs['file_name'], chain=chain, tx_descriptions="\n".join(chunk['description']).strip())
77
+ for chunk in np.array_split(tx_list, tx_list.shape[0] // TX_PER_LLM_RUN + 1)]
78
+
79
+ # Gather results and extract (valid) outputs
80
+ # The results variable is a list of 'results', each 'result' being the output of a single LLM run
81
+ results = await asyncio.gather(*tasks)
82
+
83
+ # Extract valid results (each valid result is a list of description-category pairs)
84
+ valid_results = [result['output'] for result in results if result['valid']]
85
+
86
+ # Flatten the list of valid results to obtain a single list of description-category pairs
87
+ valid_outputs = [output for valid_result in valid_results for output in valid_result]
88
+
89
+ # Return a DataFrame with the valid outputs
90
+ return pd.DataFrame(valid_outputs, columns=['description', 'category'])
91
+
92
+
93
+ @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
94
+ async def llm_sublist_categorizer(
95
+ file_name: str,
96
+ chain: LLMChain,
97
+ tx_descriptions: str,
98
+ ) -> Dict[str, Union[bool, List[Tuple[str, str]]]]:
99
+ """Categorize a batch of transactions using a language model.
100
+
101
+ This function takes a batch of transaction descriptions and passes them to a language model
102
+ for categorization. The function retries on failure, with an exponential backoff.
103
+
104
+ Args:
105
+ file_name (str): Name of the file the transaction descriptions were extracted from.
106
+ chain (LLMChain): Language model chain to use for categorization.
107
+ tx_descriptions (str): Concatenated transaction descriptions to categorize.
108
+
109
+ Returns:
110
+ dict: Dictionary containing a 'valid' flag and a list of categorized descriptions.
111
+ """
112
+
113
+ raw_result = await chain.arun(input_data=tx_descriptions)
114
+
115
+ logger = logging.getLogger(__name__)
116
+ result = {'valid': True, 'output': []}
117
+ try:
118
+ # Create a pattern to match a list Description-Category pairs (List[Tuple[str, str]])
119
+ pattern = r"\['([^']+)', '([^']+)'\]"
120
+
121
+ # Use it to extract all the correctly formatted pairs from the raw result
122
+ matches = re.findall(pattern, raw_result.replace("\\'", "'"))
123
+
124
+ # Loop over the matches, and try to parse them to ensure the content is valid
125
+ valid_outputs = []
126
+ for match in matches:
127
+ try:
128
+ parsed_pair = ast.literal_eval(str(list(match)))
129
+ valid_outputs.append(parsed_pair)
130
+ except Exception as e:
131
+ logger.log(logging.ERROR, f"Parsing Error: {e}\nMatch: {match}\n")
132
+ result['valid'] = False
133
+
134
+ result['output'] = valid_outputs
135
+
136
+ except Exception as e:
137
+ logging.log(logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}\nRaw Result: {raw_result}")
138
+ result['valid'] = False
139
+
140
+ return result
app/categorization/categorizer_list.py CHANGED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Standard library imports
2
+ import os
3
+ from datetime import datetime
4
+
5
+ # Third-party library imports
6
+ import pandas as pd
7
+
8
+ # Local application/library specific imports
9
+ from config import CATEGORY_REFERENCE_OUTPUT_FILE
10
+ from categorizer import llm_list_categorizer, fuzzy_match_list_categorizer
11
+
12
+
13
+ async def categorize_list(tx_list: pd.DataFrame) -> pd.DataFrame:
14
+ """Asynchronously categorize a list of transactions.
15
+
16
+ This function categorizes a list of transactions using a combination of fuzzy matching
17
+ and a language model. It looks up new transaction descriptions in the reference file
18
+ (a combination of user input and previous executions) to minimize API calls.
19
+ Any uncategorized transactions are sent to the language model, and new description-category
20
+ pairs are added to the reference file.
21
+
22
+ Args:
23
+ tx_list (pd.DataFrame): The list of transactions to categorize.
24
+
25
+ Returns:
26
+ pd.DataFrame: The original DataFrame with an additional column for the category.
27
+ """
28
+
29
+ if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
30
+ # Read description-category pairs from the reference file
31
+ description_category_pairs = pd.read_csv(
32
+ CATEGORY_REFERENCE_OUTPUT_FILE, header=None, names=['description', 'category']
33
+ )
34
+
35
+ # Extract only descriptions for faster matching
36
+ descriptions = description_category_pairs['description'].values
37
+
38
+ # Use fuzzy matching to find similar descriptions and assign the category
39
+ tx_list['category'] = tx_list['description'].apply(
40
+ fuzzy_match_list_categorizer,
41
+ args=(descriptions, description_category_pairs),
42
+ )
43
+
44
+ # Filter out uncategorized transactions, deduplicate, and sort by description
45
+ uncategorized_descriptions = (
46
+ tx_list[tx_list['category'].isnull()]
47
+ .drop_duplicates(subset=['description'])
48
+ .sort_values(by=['description'])
49
+ )
50
+
51
+ # Ask the language model to categorize the remaining descriptions
52
+ if not uncategorized_descriptions.empty:
53
+ categorized_descriptions = await llm_list_categorizer(
54
+ uncategorized_descriptions[['description', 'category']]
55
+ )
56
+
57
+ categorized_descriptions.dropna(inplace=True)
58
+
59
+ # Update the category for uncategorized transactions based on the language model results
60
+ if not categorized_descriptions.empty:
61
+ tx_list['category'] = tx_list['category'].fillna(
62
+ tx_list['description'].map(
63
+ categorized_descriptions.set_index('description')['category']
64
+ )
65
+ )
66
+
67
+ # Fill remaining NaN values in 'category' with 'Other'
68
+ tx_list['category'] = tx_list['category'].fillna('Other')
69
+
70
+ return tx_list
app/categorization/config.py CHANGED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Data Folders
2
+ RESULT_OUTPUT_FILE = 'data/tx_data/output/result_master_data.csv'
3
+ CATEGORY_REFERENCE_OUTPUT_FILE = 'data/ref_data/category_ref_master_data.csv'
4
+
5
+ # LLM CONFIG
6
+ TX_PER_LLM_RUN = 10
app/categorization/file_processing.py CHANGED
@@ -10,7 +10,7 @@ import pandas as pd
10
  from dateparser import parse
11
 
12
  from categorizer_list import categorize_list
13
- from config import TEXT_OUTPUT_FILE, CATEGORY_REFERENCE_OUTPUT_FILE
14
 
15
  # Read file and process it (e.g. categorize transactions)
16
  async def process_file(file_path: str) -> Dict[str, Union[str, pd.DataFrame]]:
@@ -100,7 +100,7 @@ def save_results(results: List) -> None:
100
  error_messages.append(f"{result['file_name']}: {result['error']}")
101
 
102
  # Write contents to output file (based on file type)
103
- tx_list.to_csv(TEXT_OUTPUT_FILE, mode="a", index=False, header=not os.path.exists(TEXT_OUTPUT_FILE))
104
 
105
  new_ref_data = tx_list[['name/description', 'category']]
106
  if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
 
10
  from dateparser import parse
11
 
12
  from categorizer_list import categorize_list
13
+ from config import RESULT_OUTPUT_FILE, CATEGORY_REFERENCE_OUTPUT_FILE
14
 
15
  # Read file and process it (e.g. categorize transactions)
16
  async def process_file(file_path: str) -> Dict[str, Union[str, pd.DataFrame]]:
 
100
  error_messages.append(f"{result['file_name']}: {result['error']}")
101
 
102
  # Write contents to output file (based on file type)
103
+ tx_list.to_csv(RESULT_OUTPUT_FILE, mode="a", index=False, header=not os.path.exists(RESULT_OUTPUT_FILE))
104
 
105
  new_ref_data = tx_list[['name/description', 'category']]
106
  if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
app/categorization/template.py CHANGED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CATEGORY_TEMPLATE = """
2
+ <context>
3
+ Your task is to create a list of [description, category] lists, where in each list item:
4
+ - the description is exactly the same you receive as input
5
+ - the category of the transaction (choose one from the list below based on the description)
6
+ </context>
7
+
8
+
9
+ <categories>
10
+ The following list contains the categories and associated keywords you often see in transaction descriptions:
11
+ -ATM: atm, cash, withdraw
12
+ -Auto: auto body
13
+ -Bars: bar, pubs, irish, brewery
14
+ -Beauty: body
15
+ -Cashback: cashback, reward, bonus, cash back
16
+ -Clothing: clothing, shoes, accessories
17
+ -Coffee Shops: coffee, cafe, tea, Starbucks, Dunkin
18
+ -Credit Card Payment: card payment, autopay
19
+ -Education: kindle, tuition
20
+ -Entertainment: event, show, movies, cinema, theater
21
+ -Fees: Fee
22
+ -Food: snack, Donalds, Burger King, KFC, Subway, Pizza, Domino, Taco Bell, Wendy, Chick-fil-A, Popeyes, Arby's, Chipotle
23
+ -Fuel: fuel, gas, petrol
24
+ -Gifts: donation, gift
25
+ -Groceries: groceries, supermarket, food, familia
26
+ -Gym: gym, fitness, yoga, pilates, crossfit
27
+ -Home: Ikea
28
+ -Housing: rent, mortgage
29
+ -Income: refund, deposit, paycheck
30
+ -Insurance: insurance
31
+ -Medical: medical, doctor, dentist, hospital, clinic
32
+ -Pets: vet, veterinary, pet, dog, cat
33
+ -Pharmacy: pharmacy, drugstore, cvs, walgreens, rite aid, duane
34
+ -Restaurants: restaurant, lunch, dinner
35
+ -Services: service, laundry, dry cleaning
36
+ -Shopping: shopping, amazon, walmart, target, safeway
37
+ -Streaming: Netflix, Spotify, Hulu, HBO
38
+ -Taxes: tax, irs
39
+ -Technology: technology, software, hardware, electronics
40
+ -Transportation: bus, train, subway, metro, airline, uber, lyft, taxi
41
+ -Travel: travel, holiday, trip, airbnb, kiwi, hotel, hostel, resort, kiwi, kayak, expedia, booking.com
42
+ -Transfer: payment from
43
+ -Utilities: electricity, water, gas, ting, verizon, comcast, sprint, t-mobile, at&t, mint
44
+ -Other: use this when very uncertain about the category
45
+ </categories>
46
+
47
+ <formatting_instructions>
48
+ Your output should be a valid list of lists (e.g. [[description1, category1], [description2, category2], ...]] parse-able by the command ast.literal_eval(output)
49
+ Don't include any kind of commentary, return carriages, spaces or other characters in the output
50
+ Fill all categories; if you don't know which one to choose, choose 'Other'
51
+ </formatting_instructions>
52
+
53
+
54
+ <financial_transactions>
55
+ {input_data}
56
+ </financial_transactions>"""
app/transactions_rag/categorize_transactions.ipynb CHANGED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 2,
6
+ "metadata": {},
7
+ "outputs": [
8
+ {
9
+ "name": "stdout",
10
+ "output_type": "stream",
11
+ "text": [
12
+ "Defaulting to user installation because normal site-packages is not writeable\n",
13
+ "Requirement already satisfied: pandas in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (2.0.3)\n",
14
+ "Requirement already satisfied: python-dateutil>=2.8.2 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (2.9.0.post0)\n",
15
+ "Requirement already satisfied: pytz>=2020.1 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (2024.1)\n",
16
+ "Requirement already satisfied: tzdata>=2022.1 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (2024.1)\n",
17
+ "Requirement already satisfied: numpy>=1.20.3 in /Users/patrickalexis/Library/Python/3.8/lib/python/site-packages (from pandas) (1.24.4)\n",
18
+ "Requirement already satisfied: six>=1.5 in /Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/site-packages (from python-dateutil>=2.8.2->pandas) (1.15.0)\n"
19
+ ]
20
+ },
21
+ {
22
+ "data": {
23
+ "text/html": [
24
+ "<div>\n",
25
+ "<style scoped>\n",
26
+ " .dataframe tbody tr th:only-of-type {\n",
27
+ " vertical-align: middle;\n",
28
+ " }\n",
29
+ "\n",
30
+ " .dataframe tbody tr th {\n",
31
+ " vertical-align: top;\n",
32
+ " }\n",
33
+ "\n",
34
+ " .dataframe thead th {\n",
35
+ " text-align: right;\n",
36
+ " }\n",
37
+ "</style>\n",
38
+ "<table border=\"1\" class=\"dataframe\">\n",
39
+ " <thead>\n",
40
+ " <tr style=\"text-align: right;\">\n",
41
+ " <th></th>\n",
42
+ " <th>Date</th>\n",
43
+ " <th>Name / Description</th>\n",
44
+ " <th>Expense/Income</th>\n",
45
+ " <th>Amount</th>\n",
46
+ " </tr>\n",
47
+ " </thead>\n",
48
+ " <tbody>\n",
49
+ " <tr>\n",
50
+ " <th>0</th>\n",
51
+ " <td>2023-12-30</td>\n",
52
+ " <td>Comcast Internet</td>\n",
53
+ " <td>Expense</td>\n",
54
+ " <td>9.96</td>\n",
55
+ " </tr>\n",
56
+ " <tr>\n",
57
+ " <th>1</th>\n",
58
+ " <td>2023-12-30</td>\n",
59
+ " <td>Lemonade Home Insurance</td>\n",
60
+ " <td>Expense</td>\n",
61
+ " <td>17.53</td>\n",
62
+ " </tr>\n",
63
+ " <tr>\n",
64
+ " <th>2</th>\n",
65
+ " <td>2023-12-30</td>\n",
66
+ " <td>Monthly Appartment Rent</td>\n",
67
+ " <td>Expense</td>\n",
68
+ " <td>2000.00</td>\n",
69
+ " </tr>\n",
70
+ " <tr>\n",
71
+ " <th>3</th>\n",
72
+ " <td>2023-12-30</td>\n",
73
+ " <td>Staples Office Supplies</td>\n",
74
+ " <td>Expense</td>\n",
75
+ " <td>12.46</td>\n",
76
+ " </tr>\n",
77
+ " <tr>\n",
78
+ " <th>4</th>\n",
79
+ " <td>2023-12-29</td>\n",
80
+ " <td>Selling Paintings</td>\n",
81
+ " <td>Income</td>\n",
82
+ " <td>13.63</td>\n",
83
+ " </tr>\n",
84
+ " </tbody>\n",
85
+ "</table>\n",
86
+ "</div>"
87
+ ],
88
+ "text/plain": [
89
+ " Date Name / Description Expense/Income Amount\n",
90
+ "0 2023-12-30 Comcast Internet Expense 9.96\n",
91
+ "1 2023-12-30 Lemonade Home Insurance Expense 17.53\n",
92
+ "2 2023-12-30 Monthly Appartment Rent Expense 2000.00\n",
93
+ "3 2023-12-30 Staples Office Supplies Expense 12.46\n",
94
+ "4 2023-12-29 Selling Paintings Income 13.63"
95
+ ]
96
+ },
97
+ "execution_count": 2,
98
+ "metadata": {},
99
+ "output_type": "execute_result"
100
+ }
101
+ ],
102
+ "source": [
103
+ "# Read the transactions csv\n",
104
+ "!pip3 install pandas\n",
105
+ "\n",
106
+ "import pandas as pd\n",
107
+ "df = pd.read_csv(\"transactions_2024.csv\")\n",
108
+ "df.head()"
109
+ ]
110
+ },
111
+ {
112
+ "cell_type": "code",
113
+ "execution_count": 3,
114
+ "metadata": {},
115
+ "outputs": [
116
+ {
117
+ "data": {
118
+ "text/plain": [
119
+ "array(['Lemonade Home Insurance', 'Monthly Appartment Rent',\n",
120
+ " 'Staples Office Supplies', 'Selling Paintings', 'Spotify',\n",
121
+ " 'Target', 'IT Consulting', 'Phone', 'ML Consulting'], dtype=object)"
122
+ ]
123
+ },
124
+ "execution_count": 3,
125
+ "metadata": {},
126
+ "output_type": "execute_result"
127
+ }
128
+ ],
129
+ "source": [
130
+ "# Get Unique transactions in the Name/Description column\n",
131
+ "unique_transactions = df[\"Name / Description\"].unique()\n",
132
+ "unique_transactions[1:10]"
133
+ ]
134
+ },
135
+ {
136
+ "cell_type": "code",
137
+ "execution_count": 1,
138
+ "metadata": {},
139
+ "outputs": [
140
+ {
141
+ "ename": "ModuleNotFoundError",
142
+ "evalue": "No module named 'categorization'",
143
+ "output_type": "error",
144
+ "traceback": [
145
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
146
+ "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
147
+ "Cell \u001b[0;32mIn[1], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Process the transactions csv and get the results from the categorization llm utility\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mcategorization\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mfile_processing\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m process_file, save_results\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mdotenv\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m load_dotenv\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01masyncio\u001b[39;00m\n",
148
+ "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'categorization'"
149
+ ]
150
+ }
151
+ ],
152
+ "source": [
153
+ "# Process the transactions csv and get the results from the categorization llm utility\n",
154
+ "from categorization.file_processing import process_file, save_results\n",
155
+ "from dotenv import load_dotenv\n",
156
+ "import asyncio\n",
157
+ "\n",
158
+ "load_dotenv()\n",
159
+ "\n",
160
+ "async def apply_categorization():\n",
161
+ " processed_file = process_file(\"transactions_2024.csv\")\n",
162
+ "\n",
163
+ " print(\"\\nProcessing file\")\n",
164
+ " result = await asyncio.gather(processed_file)\n",
165
+ "\n",
166
+ " save_results(results)\n",
167
+ " print(results)\n",
168
+ " \n",
169
+ "\n",
170
+ "asyncio.run(apply_categorization())"
171
+ ]
172
+ }
173
+ ],
174
+ "metadata": {
175
+ "kernelspec": {
176
+ "display_name": "Python 3.8.9 64-bit",
177
+ "language": "python",
178
+ "name": "python3"
179
+ },
180
+ "language_info": {
181
+ "codemirror_mode": {
182
+ "name": "ipython",
183
+ "version": 3
184
+ },
185
+ "file_extension": ".py",
186
+ "mimetype": "text/x-python",
187
+ "name": "python",
188
+ "nbconvert_exporter": "python",
189
+ "pygments_lexer": "ipython3",
190
+ "version": "3.8.9"
191
+ },
192
+ "orig_nbformat": 4,
193
+ "vscode": {
194
+ "interpreter": {
195
+ "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
196
+ }
197
+ }
198
+ },
199
+ "nbformat": 4,
200
+ "nbformat_minor": 2
201
+ }
app/transactions_rag/transactions_2024.csv CHANGED
@@ -25,5 +25,5 @@ Date,Name / Description,Expense/Income,Amount
25
  2022-10-22,Apple Services,Expense,41.25
26
  2022-10-21,Netflix,Expense,22.8
27
  2022-01-16,Amazon Lux,Expense,24.11
28
- 2022-01-15,Classpass* Monthly,Expense,30.08
29
- 2022-01-14,Amazon,Expense,11.0
 
25
  2022-10-22,Apple Services,Expense,41.25
26
  2022-10-21,Netflix,Expense,22.8
27
  2022-01-16,Amazon Lux,Expense,24.11
28
+ 2022-01-15,Burger King,Expense,30.08
29
+ 2022-01-14,Amazon,Expense,11.0
requirements.txt CHANGED
@@ -8,4 +8,14 @@ llama-index-vector-stores-pinecone==0.1.3
8
  llama-index==0.10.28
9
  python-dotenv==1.0.0
10
  traceloop-sdk==0.15.11
11
- uvicorn==0.23.2
 
 
 
 
 
 
 
 
 
 
 
8
  llama-index==0.10.28
9
  python-dotenv==1.0.0
10
  traceloop-sdk==0.15.11
11
+ uvicorn==0.23.2
12
+
13
+ langchain
14
+ python-dotenv
15
+ openai
16
+ tenacity
17
+ rapidfuzz
18
+ pydantic
19
+ dateparser
20
+ pandas
21
+ path