Docs cleanup

#1
by kenleeyx - opened
.gitignore DELETED
@@ -1,4 +0,0 @@
1
- venv/
2
- .env
3
- output.xlsx
4
- .gradio/
 
 
 
 
 
README.md CHANGED
@@ -8,50 +8,6 @@ sdk_version: 5.9.1
8
  app_file: app.py
9
  pinned: false
10
  short_description: Tags phrases from study respondents.
11
- python_version: 3.13.1
12
  ---
13
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
15
-
16
- # TFT Tagging app by Kenneth Lee, PercepTech.AI
17
-
18
- ## What it does
19
- Uses a LLM to tag each quote in a column of an Excel file with zero or more tags from a given list, and returns the quotes tags on the app and also in a new file, along with some statistics and translations of randomly selected quotes for each tag.
20
-
21
- Inputs:
22
- -Single sheet Excel file
23
- -Column header to identify which column of data should be tagged
24
- -List of tags to be assigned to the data
25
- -List of columns to retain in the output file(these will not be changed from the input file)
26
-
27
- Outputs:
28
- -List of quotes and tagged tags
29
- -Count of number of instances tagged for eachtag
30
- -Excel file containing the above data together with translations of some quotes corresponding to each tag that was used
31
-
32
- ## Notables
33
- ### Authentication:
34
- - Private spaces require a HF account to access, hence this space is set as public. However anyone without a username and password will be stuck at a login screen.
35
- - The username and password are stored as secrets on HF itself. You may also refer to emails I have sent.
36
- - A temporary username, password, and login expiry time may be set in the secrets in order to give temporary access to TFT contractors. The time should be entered as Singapore time in ISO8601 format and any logins with the temporary account after this time will be prevented.
37
- Progress bar only takes into account tagging time and not translation time; thus it may hang for a short while at 100% while doing translation.
38
-
39
- ### Quality of tagging:
40
- - Ideally we want the LLM's tags to match exactly what a trained market researcher would tag. There are however two difficulties in this:
41
- 1)even two market researchers will not give identical tags for the same tags and the same dataset, and
42
- 2)the LLM has some variation in tags even when running the same tags and dataset twice, due to its non-deterministic nature.
43
- - As such, to address the above difficulties, we assessed the quality of the LLM's tags by calculating precision, recall, and F1 of 8 aggregated runs of the LLM vs a trained market researcher(credit to Yong Li of TFT for providing the data), and also by calculating these for myself vs Yong Li.
44
- - By doing so we are able to get an estimate of the amount of agreement between humans on the same tags and dataset, and if the LLM is able to match this on average, then it can be considered to be equal to a human for this task.
45
- - We achieved an F1 of approximately 90 for myself vs Yong Li and about 70 for the AI vs Yong Li, indicating that the AI has room for improvement.
46
- - Refer to Perceptech AI driver/TFT tagging app reference material for the data
47
-
48
- ### Sample input file and corresponding output:
49
- - Refer to Perceptech AI driver/TFT tagging app reference material
50
-
51
- ### Branching behaviour:
52
- - HF doesn't allow storage of any branches other than main online, to my knowledge. As such I usually update the repo by git push origin main - so be sure it works before pushing!
53
-
54
- ## Future work
55
- - In the future we hope to improve the tagging so that the LLM-human F1 score matches human-human F1 scores.
56
- - I had explored fine tuning using 10 responses from the OE US dataset and Yong Li's model answers; this improves performance on the training set from F1 70 to F1 85 but I do not have a test set, the model name is "ft:gpt-4o-mini-2024-07-18:percepsense::BsDwvH1E"; just substitute it where the model eg "gpt-4o-mini" is specified
57
- - If TFT is able to retrieve any historical tagging data of their own human researchers on survey responses, this may be good training material also.
 
8
  app_file: app.py
9
  pinned: false
10
  short_description: Tags phrases from study respondents.
 
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -1,283 +1,145 @@
1
- # Import necessary modules
2
- import gradio as gr # For creating the interactive user interface
3
- import os # For accessing environment variables
4
- import pandas as pd # For easier data handling
5
- from openai import OpenAI # For sending the quotes to OpenAI for tagging
6
- import openpyxl # Requirement for reading Excel files into pandas Dataframes
7
- import json # For conversion of OpenAI responses into json/dictionary objects so the contents can be extracted
8
- from dotenv import load_dotenv # For loading environment variables in local environment
9
- from collections import Counter # For tabulating tag occurrences
10
- import logging
11
- import time
12
- import random
13
- from datetime import datetime
14
- from typing import Generator
15
- from concurrent.futures import ThreadPoolExecutor, as_completed
16
 
17
- logger = logging.getLogger()
18
- logger.setLevel(logging.INFO)
19
- logging.basicConfig(level=logging.INFO, force=True)
20
-
21
- # Load environment variables from local .env file if it exists; otherwise this does nothing
22
- load_dotenv()
23
-
24
- # Import prompt for requesting the tags from OpenAI
25
- with open("prompts/prompt_030725.txt", "r") as prompt_file:
26
- PROMPT = prompt_file.read()
27
- logger.info(f"Loaded prompt: {PROMPT}")
28
-
29
- # Import user instructions for display on screen
30
- with open("user_instructions.txt", "r") as user_instruction_file:
31
- INSTRUCTIONS = user_instruction_file.read()
32
-
33
- #Initialising the OpenAI client
34
  client = OpenAI(
35
  api_key=os.getenv('OPENAI_KEY'),
36
  organization=os.getenv('ORG_KEY'),
37
  project=os.getenv('PROJ_KEY')
38
  )
39
- logger.info("Initialised OpenAI client")
40
-
41
- # Function to send the prompt with quote and tag list to OpenAI and get the tags for that quote back
42
- def tag_quote(quote: str, tags_list: list) -> list:
43
- """
44
- Generates a list of tags for a given quote based on a predefined list of potential tags.
45
-
46
- This function uses a GPT-based language model to analyze the input quote and determine
47
- the most relevant tags from the provided list. The response is parsed from the JSON
48
- output of the model and returned as a list of tags. This list is checked to ensure
49
- all tags tagged are taken from the input tags_list.
50
 
51
- Args:
52
- quote (str): The quote or text to be analyzed.
53
- tags_list (list): A list of potential tags to match against the quote.
54
-
55
- Returns:
56
- valid_tags: A list of tags that are relevant to the quote, as determined by the model.
57
- """
58
- logger.info(f"Tagging quote {quote}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  response = client.chat.completions.create(
60
  model = "gpt-4o-mini",
61
  response_format={"type": "json_object"},
62
  messages=[
63
  {"role": "system", "content": "You are a helpful assistant designed to output JSON."},
64
- {"role": "user", "content": PROMPT.format(tags_list=tags_list, quote=quote)}
65
  ]
66
  )
67
-
68
- tags = json.loads(response.choices[0].message.content)['tags']
69
- valid_tags = []
70
- for tag in tags: # filter out any hallucinated tags
71
- if tag in tags_list:
72
- valid_tags.append(tag)
73
- else:
74
- logger.warning(f"Invalid tag {tag} found and has been filtered out.")
75
- return valid_tags
76
-
77
- def translate_quote(quote: str) -> str:
78
- """
79
- Translates a quote to English.
80
- """
81
- logger.info(f"Translating quote {quote}")
82
- response = client.chat.completions.create(
83
- model = "gpt-4o-mini",
84
- messages=[
85
- {"role": "user", "content": f"Translate the following quote into English. Do not return anything other than the translated quote. {quote}"}
86
- ]
87
- )
88
- logger.info("Content")
89
- logger.info(response.choices[0].message.content)
90
- return response.choices[0].message.content
91
-
92
- def count_tags(tags_list: list, tags_col: pd.Series )->pd.DataFrame:
93
- """
94
- Creates a DataFrame indicating number of occurences of each tag from a DataFrame column containing lists of tags.
95
-
96
- This function also takes in a tags_list; all tags in the tags_list will be in the output
97
- DataFrame even if they do not occur in the input tags_col. There may be some tags appearing
98
- in the output which were not in the original tag_list; these will be marked with a ! prefix.
99
-
100
- Args:
101
- tags_list (list): The list of tags given by the user
102
- tags_col (pd.Series): A column of lists where each list contains tags which are
103
- (ideally but not always; depending on OpenAI) selected from the tags_list.
104
 
105
- Returns:
106
- pd.DataFrame: A DataFrame with two columns. The first contains individual tags(str) which have
107
- appeared either in the tags_list, the lists within the tags_col, or both. The second
108
- contains the number of occurrences(int) of that tag within the lists in the tags_col.
109
- """
110
- # Initialise Counter hash table
111
- tags_counter = Counter({tag: 0 for tag in tags_list})
112
- # Iterate over the lists in tags_col
113
- for sublist in tags_col:
114
- # Iterate over the tags in each list
115
- for tag in sublist:
116
- # Update the tags_counter for each tag
117
- if tag in tags_list:
118
- tags_counter.update([tag])
119
- # If the tag was not in the tags_list given by the user, prefix it with a ! before updating
120
- else:
121
- tags_counter.update([f"!{tag}"])
122
- # Convert the tags_counter to a DataFrame and return it
123
- tags_counter_df = pd.DataFrame(tags_counter.items(), columns=['Tag', 'Count'])
124
- return tags_counter_df
125
-
126
-
127
- # Function that takes in a list of tags and an Excel file of quotes, calls tag_quote() on each quote, and returns all the quotes and tags in a DataFrame
128
- def process_quotes(quotes_file_path: str, quotes_col_name: str, retained_columns: str, tags_string: str) -> Generator[tuple[str, pd.DataFrame, pd.DataFrame, str]]:
129
- """
130
- Processes quotes from an Excel file and assigns relevant tags to each quote.
131
-
132
- This function reads an Excel file containing quotes, validates the column containing
133
- the quotes, and applies the `tag_quote` function to assign tags to each quote.
134
- The tags are derived from a user-provided newline-separated string.
135
-
136
- Args:
137
- quotes_file_path (str): Path to the Excel file containing the quotes.
138
- quotes_col_name (str): The name of the column containing the quotes.
139
- retained_columns (str): The names of the columns in the Excel file which are to be added to the output file.
140
- tags_string (str): A newline-separated string of potential tags.
141
-
142
- Yields:
143
- tuple: A 4-element tuple containing:
144
- - str: A progress indicator (or "Not running" if tagging is complete)
145
- - pd.DataFrame: A DataFrame with two columns: (or None if tagging is incomplete)
146
- - The original column containing the quotes.
147
- - A new column 'Tags' with the tags assigned to each quote.
148
- - pd.DataFrame: A DataFrame with two columns: (or None if tagging is incomplete)
149
- -"Tag" - The list of tags that was passed in.
150
- -"Count" - The total number of times each tag was used in tagging all the quotes.
151
- - str: A path to an Excel file containing sheets derived from the previous 2 DataFrames. (or None if tagging is incomplete)
152
-
153
- Raises:
154
- gr.Error: If the specified column name does not exist or is not unique.
155
- """
156
- tags_list = tags_string.split('\n')
157
  tags_list = [tag.strip() for tag in tags_list]
158
 
159
- if retained_columns:
160
- retained_cols_list = retained_columns.split(',')
161
- retained_cols_list = [colname.strip() for colname in retained_cols_list]
162
- else:
163
- retained_cols_list = []
164
-
165
- # Transfer quotes data from Excel file into pandas DataFrame, handling potential duplicate column names in the Excel file
166
- # pd.read_excel will rename duplicates eg foo -> foo.1, causing a mismatch between quotes_col_name and the actual column name
167
- # Extract the first row(the actual header for the DataFrame) as a DataFrame without header.
168
- quotes_df_cols= pd.read_excel(quotes_file_path, header=None, nrows=1).values[0]
169
- # Extract all the other rows of the Excel file as a DataFrame without header
170
- quotes_df = pd.read_excel(quotes_file_path, header=None, skiprows=1)
171
- # Set the extracted first row as the header for the DataFrame resultant from the other rows
172
- quotes_df.columns = quotes_df_cols
173
- # Verify that all column names given are found in the quotes DF exactly once each
174
- for colname in retained_cols_list + [quotes_col_name]:
175
- count = quotes_df.columns.tolist().count(colname)
176
- if count == 0:
177
- raise gr.Error(f"No columns with name {colname} found, check your inputs")
178
- elif count > 1:
179
- raise gr.Error(f"Multiple columns with name {colname} found, please rename these columns to something unique")
180
-
181
  quotes_data = quotes_df[quotes_col_name]
 
 
182
 
183
- # Tag all the quotes one by one using tag_quote function
184
- tags_results = [None]*len(quotes_data)
185
-
186
- # Threading execution of tag_quotes with {max_workers} threads: we send {max_workers} requests to the LLM concurrently.
187
- with ThreadPoolExecutor() as executor:
188
- # Generate futures for each of the quotes and map them to the quote indices
189
- future_to_index = {
190
- executor.submit(tag_quote, quote, tags_list): i for i, quote in enumerate(quotes_data)
191
- }
192
- # Enumerate the completed futures(ordered as completed which may be different from submitted order)
193
- # This step waits for the tag_quote functions to complete
194
- for completed, future in enumerate(as_completed(future_to_index), 1):
195
- # Retrieve index of the completed future from above map
196
- i = future_to_index[future]
197
- # Insert the result of the completed future into the results list at its quote's original position
198
- try:
199
- tags_results[i] = future.result()
200
- except Exception as e:
201
- tags_results[i] = f"Error:{e}"
202
- # Update UI by yielding a status update
203
- yield (f"Tagged {completed}/{len(quotes_data)} quotes: {quotes_data[i]}", None, None, None)
204
-
205
- quotes_df['Tags'] = tags_results
206
-
207
- # One hot encoding of tagged tags
208
- for tag in tags_list:
209
- quotes_df[tag]=quotes_df['Tags'].apply(lambda quote_tags: int(tag in quote_tags))
210
- logger.info("Quotes tagged")
211
-
212
- # Create hash table of tag occurrences using count_tags function
213
- tags_counter_df = count_tags(tags_list, quotes_df['Tags'])
214
-
215
- # Retrieve 2 quotes at random for each tag and put them in the tags counter DF
216
- for tag in tags_counter_df['Tag']:
217
- tagged_quotes_list = quotes_df.loc[quotes_df[tag]==1, quotes_col_name].tolist()
218
- sample_quotes = random.sample(tagged_quotes_list, min(2, len(tagged_quotes_list)))
219
- translated_quotes = [translate_quote(quote) for quote in sample_quotes]
220
- while len(sample_quotes) < 2:
221
- sample_quotes.append(None)
222
- translated_quotes.append(None)
223
- [tags_counter_df.loc[tags_counter_df['Tag'] == tag, 'Quote 1'], tags_counter_df.loc[tags_counter_df['Tag'] == tag, 'Quote 2']] = sample_quotes
224
- [tags_counter_df.loc[tags_counter_df['Tag'] == tag, 'Translated Quote 1'], tags_counter_df.loc[tags_counter_df['Tag'] == tag, 'Translated Quote 2']] = translated_quotes
225
-
226
- #Convert values in tags column from list to str
227
- quotes_df['Tags'] = quotes_df["Tags"].apply(lambda x: ", ".join(x))
228
-
229
- # Return only the quotes column, the new tags column, and any other specified cols to retain
230
- output_df = quotes_df[retained_cols_list+[quotes_col_name, 'Tags']+tags_list]
231
- output_file_path = "output.xlsx"
232
- with pd.ExcelWriter(output_file_path) as writer:
233
- output_df.to_excel(writer, sheet_name='Coded Quotes', index=False)
234
- tags_counter_df.to_excel(writer, sheet_name='Tag Count', index=False)
235
- logger.info('Results written to Excel')
236
- yield ("Not running", output_df[[quotes_col_name, 'Tags']], tags_counter_df, output_file_path)
237
-
238
- def check_auth(username:str, password:str):
239
- """
240
- Authenticate the user.
241
-
242
- Verifies the user's credentials against the values stored in the environment variables.
243
- User may authenticate with permanent username and password(for TFT team) or temporary username and password.
244
- For temporary username and password, they will only be valid before the expiry time as set in the environment variables.
245
-
246
- Returns True or False depending on authentication success.
247
- """
248
- # Check permanent credentials
249
- if username == os.getenv('APP_USERNAME') and password == os.getenv('APP_PASSWORD'):
250
- return True
251
-
252
- # Check temporary credentials
253
- if (
254
- username == os.getenv('TEMP_USERNAME') and
255
- password == os.getenv('TEMP_PASSWORD') and
256
- time.time() < datetime.fromisoformat(os.getenv('TEMP_EXPIRY_TIME_SG_ISO_8601').replace("Z", "+08:00")).timestamp()
257
- ):
258
- return True
259
-
260
- # Invalid credentials
261
- return False
262
-
263
- # Define user interface structure
264
  demo = gr.Interface(
265
  fn=process_quotes,
266
  inputs=[
267
- gr.File(label="Quotes Excel File"),
268
- gr.Textbox(label="Name of quotes column"),
269
- gr.Textbox(label = "Names of columns(eg respondentID) to retain in output, separated by commas"),
270
- gr.Textbox(label = "List of tags, each tag on a new line"),
271
  ],
272
- outputs=[
273
- gr.Textbox(label="Progress", value = "Not running"),
274
- gr.Dataframe(headers=["Quote", "Tags"], column_widths=["70%", "30%"], scale=2, label='Coded Quotes'),
275
- gr.Dataframe(headers=["Tag", "Count"], label='Tag Count'),
276
- gr.File(label="Output data in file format")
277
-
278
- ],
279
- title="Automated Research Code Tagger",
280
- description=INSTRUCTIONS
281
  )
282
 
283
- demo.launch(share=True, auth=check_auth, ssr_mode=False)
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import pandas as pd
4
+ from openai import OpenAI
5
+ import openpyxl
6
+ import json
 
 
 
 
 
 
 
 
 
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  client = OpenAI(
9
  api_key=os.getenv('OPENAI_KEY'),
10
  organization=os.getenv('ORG_KEY'),
11
  project=os.getenv('PROJ_KEY')
12
  )
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # Static Username and Password
15
+ VALID_USERNAME = "tft@perceptech.ai"
16
+ VALID_PASSWORD = "Perceptech@2024!"
17
+
18
+ #need to give info on how to convert to CSV
19
+ title = "Automated Research Code Tagger"
20
+ description = """
21
+ ABOUT:\n
22
+ This automated tagger takes in a list of tags and a list of input quotes. Each input quote is individually fed to OpenAI's ChatGPT together with the list of tags,
23
+ and ChatGPT will respond with the subset of the input tags which are related to the content of the quote.\n
24
+
25
+ HOW TO USE:\n
26
+ 1)Upload a single sheet Excel file containing quotes in a column.(It is ok for the file to contain other data also)\n
27
+ 2)Type in the name of the column where the quotes are located\n
28
+ 3)Type in a list of tags separated by commas. For proper names/slogans/other tags that should be treated as an inseparable unit eg. Nike's "Just Do It", add a * in front of the tag eg. tag1, *Just Do It, tag3, etc.
29
+ This will ensure only quotes containing "Just Do It" exactly are tagged and not other quotes about doing other things.\n
30
+ 4)All the responses from ChatGPT will be collated and displayed in the table on the right, together with the original quotes.
31
+ You may then copy them into an Excel file for further processing. Please allow 5-10 min for processing, especially if you are giving upwards of 100 quotes!\n
32
+
33
+ Please bear in mind that the tags are AI generated so check your results to ensure they make sense before using them.
34
+ I will not be responsible for mistakes made by the AI, but I can try to fix them if you alert me.
35
+ -Kenneth
36
+ """
37
+
38
+ prompt = """
39
+ Given the quote below and the regular tag list below, evaluate each tag in the tag list and determine if the meaning of the quote can be described by that tag topic.
40
+ If so, return the relevant tag in your response. Use only the tags provided in the list. Under no circumstances should you create new tag names.
41
+
42
+ For the tags starting with a *, these tags should be treated as proper nouns(usually product names or slogans) and should not be used unless the quote explicitly contains the entire tag.
43
+ For quotes with meanings that are more ambiguous and can relate to multiple tags, make no assumptions about their meanings and only add tags if the topic of the tag is actually mentioned in the quote.
44
+ If there are no relevant tags to the quote, return an empty list.
45
+
46
+ Quote:
47
+ {quote}
48
+
49
+ Tag list:
50
+ {tags_list}
51
+
52
+ Respond in the following format:
53
+ {{
54
+ "tags":[<tagName1>, <tagName2>]
55
+ }}
56
+ """
57
+ def tag_quote(quote, tags_list):
58
  response = client.chat.completions.create(
59
  model = "gpt-4o-mini",
60
  response_format={"type": "json_object"},
61
  messages=[
62
  {"role": "system", "content": "You are a helpful assistant designed to output JSON."},
63
+ {"role": "user", "content": prompt.format(tags_list=tags_list, quote=quote)}
64
  ]
65
  )
66
+ print(response.choices[0].message.content)
67
+ return json.loads(response.choices[0].message.content)['tags']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ def process_quotes(quotes_file_path, quotes_col_name, tags_string):
70
+ print(quotes_file_path)
71
+ print(quotes_col_name)
72
+ print(tags_string)
73
+ tags_list = tags_string.split(',')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  tags_list = [tag.strip() for tag in tags_list]
75
 
76
+ #next 3 lines are necessary as pd.read_excel will rename duplicate columns found in the excel file eg foo -> foo.1, hence we need to extract the first row alone and not as header, and then set it as header for the rest of the DF later.
77
+ quotes_df_cols= pd.read_excel(quotes_file_path, header=None, nrows=1).values[0] #creates a df without header from the excel and takes the first row
78
+ quotes_df = pd.read_excel(quotes_file_path, header=None, skiprows=1) # converts row 2 onwards into the DF, without specifying a header
79
+ quotes_df.columns = quotes_df_cols # sets the first row of excel file as header
80
+
81
+ count = quotes_df.columns.tolist().count(quotes_col_name)
82
+ if count == 0:
83
+ raise gr.Error("No columns with this name found")
84
+ elif count > 1:
85
+ print("Count>1!!")
86
+ raise gr.Error("Multiple columns with this name found, please rename to something unique")
 
 
 
 
 
 
 
 
 
 
 
87
  quotes_data = quotes_df[quotes_col_name]
88
+ quotes_df['Tags'] = quotes_data.apply(tag_quote, args=(tags_list,))
89
+ return quotes_df[[quotes_col_name, 'Tags']]
90
 
91
+ # def authenticate(username, password):
92
+ # """Authenticate the user using static username and password"""
93
+ # if username == VALID_USERNAME and password == VALID_PASSWORD:
94
+ # return True
95
+ # else:
96
+ # return False
97
+
98
+ # def auth_interface(username, password):
99
+ # """Handle the authentication and proceed with the main function if valid"""
100
+ # if authenticate(username, password):
101
+ # return gr.Interface(
102
+ # fn=process_quotes,
103
+ # inputs=[
104
+ # gr.File(label="Quotes Excel File"), # File as generated by TFT software
105
+ # gr.Textbox(label="Name of quotes column"), # use this to identify the col with the quotes
106
+ # gr.Textbox(label="List of tags separated by commas")
107
+ # ],
108
+ # outputs=gr.Dataframe(headers=["Quote", "Tags"], column_widths=["70%", "30%"], scale=2),
109
+ # title=title,
110
+ # description=description
111
+ # ).launch()
112
+ # else:
113
+ # return "Invalid username or password!"
114
+
115
+ # # Create the authentication fields before launching the main app
116
+ # auth_app = gr.Interface(
117
+ # fn=auth_interface,
118
+ # inputs=[
119
+ # gr.Textbox(label="Username", type="text"),
120
+ # gr.Textbox(label="Password", type="password")
121
+ # ],
122
+ # outputs="text",
123
+ # title="Login to Automated Research Code Tagger",
124
+ # description="Please enter the correct username and password to access the tool."
125
+ # )
126
+
127
+ # auth_app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  demo = gr.Interface(
129
  fn=process_quotes,
130
  inputs=[
131
+ gr.File(label="Quotes Excel File"), # File as generated by TFT software
132
+ gr.Textbox(label="Name of quotes column"), # use this to identify the col with the quotes
133
+ gr.Textbox(label = "List of tags separated by commas")
 
134
  ],
135
+ outputs=gr.Dataframe(headers=["Quote", "Tags"], column_widths=["70%", "30%"], scale=2),
136
+ title=title,
137
+ description=description
 
 
 
 
 
 
138
  )
139
 
140
+ demo.launch()
141
+
142
+ # For later when I enable usage of own API key
143
+ # api_key = gr.Textbox(
144
+ # type="password", label="Enter your OpenAI API key here (Optional for Perceptech users)"
145
+ # )
prompts/base_prompt.txt DELETED
@@ -1,17 +0,0 @@
1
- Given the quote below and the regular tag list below, evaluate each tag in the tag list and determine if the meaning of the quote can be described by that tag topic.
2
- If so, return the relevant tag in your response. Use only the tags provided in the list. Under no circumstances should you create new tag names.
3
-
4
- For the tags starting with a *, these tags should be treated as proper nouns(usually product names or slogans) and should not be used unless the quote explicitly contains the entire tag.
5
- For quotes with meanings that are more ambiguous and can relate to multiple tags, make no assumptions about their meanings and only add tags if the topic of the tag is actually mentioned in the quote.
6
- If there are no relevant tags to the quote, return an empty list.
7
-
8
- Quote:
9
- {quote}
10
-
11
- Tag list:
12
- {tags_list}
13
-
14
- Respond in the following format:
15
- {{
16
- "tags":[<tagName1>, <tagName2>]
17
- }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
prompts/prompt_030725.txt DELETED
@@ -1,21 +0,0 @@
1
- Given the quote below and the regular tag list below, evaluate each tag in the tag list and determine if the meaning of the quote can be described by that tag topic.
2
- If so, return the relevant tag in your response. Use only the tags provided in the list. Under no circumstances should you create new tag names.
3
-
4
- IMPORTANT: Do NOT infer the respondent's meaning based on brand reputation, public knowledge, or commonly associated attributes (e.g., don't assume "natural" just because a brand is known for it). Only use a tag if the specific idea or topic is **explicitly stated** or clearly implied **by the respondent**, not by external associations.
5
-
6
- Tags starting with a * are proper nouns (usually product names or slogans) and should only be used if the quote contains the entire tag exactly.
7
-
8
- If the quote is vague or ambiguous, do not guess. Only add tags if the topic of the tag is clearly and unambiguously expressed in the quote.
9
-
10
- If there are no relevant tags, return an empty list.
11
-
12
- Quote:
13
- {quote}
14
-
15
- Tag list:
16
- {tags_list}
17
-
18
- Respond in the following format:
19
- {{
20
- "tags":[<tagName1>, <tagName2>]
21
- }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,5 +1,3 @@
1
- gradio==5.12.0
2
  openai==1.59.3
3
- openpyxl==3.1.5
4
- python-dotenv==1.0.1
5
- pydantic==2.10.6
 
1
+ gradio==4.31.5
2
  openai==1.59.3
3
+ openpyxl==3.1.5
 
 
user_instructions.txt DELETED
@@ -1,22 +0,0 @@
1
- ABOUT:
2
- This automated tagger takes in a list of tags and a list of input quotes. Each input quote is individually fed to OpenAI's ChatGPT together with the list of tags,
3
- and ChatGPT will respond with the subset of the input tags which are related to the content of the quote.
4
-
5
- HOW TO USE:<br>
6
- 1)Upload a single sheet Excel file containing quotes in a column.(It is ok for the file to contain other data also)<br>
7
- 2)Type in the name of the column where the quotes are located<br>
8
- 3)Type in the names of any other columns which you wish to retain in the output <br>
9
- 4)Type in a list of tags, each tag on a new line. For proper names/slogans/other tags that should be treated as an inseparable unit eg. Nike's "Just Do It", add a * in front of the tag eg. tag1, *Just Do It, tag3, etc. <br>
10
- This will ensure only quotes containing "Just Do It" exactly are tagged and not other quotes about doing other things.<br>
11
- Please allow 5-10 min for processing, especially if you are giving upwards of 100 quotes!<br>
12
-
13
- READING THE OUTPUT(in the right-side column): <br>
14
- Progress bar: Indicates which quote is currently being processed <br>
15
- First table: All the responses are collated and displayed here, together with the original quotes.<br>
16
- Second table: Displays all the tags used and the number of occurrences of each tag. <br>
17
- You may also retrieve both tables in a single Excel file via the link below them.<br>
18
-
19
- DISCLAIMER:<br>
20
- Please bear in mind that the tags are AI generated so check your results to ensure they make sense before using them.
21
- I will not be responsible for mistakes made by the AI, but I can try to fix them if you alert me.<br>
22
- -Kenneth Lee, Perceptech.AI