barunsaha commited on
Commit
ed77618
1 Parent(s): c1acf68

Fix #31: find & use similar icons when non-existing icon names are generated by the LLM

Browse files
app.py CHANGED
@@ -3,9 +3,9 @@ Streamlit app containing the UI and the application logic.
3
  """
4
  import datetime
5
  import logging
6
- import os
7
  import pathlib
8
  import random
 
9
  import tempfile
10
  from typing import List, Union
11
 
@@ -15,6 +15,10 @@ from langchain_community.chat_message_histories import StreamlitChatMessageHisto
15
  from langchain_core.messages import HumanMessage
16
  from langchain_core.prompts import ChatPromptTemplate
17
 
 
 
 
 
18
  from global_config import GlobalConfig
19
  from helpers import llm_helper, pptx_helper, text_helper
20
 
@@ -68,12 +72,7 @@ def _get_icons_list() -> List[str]:
68
  :return: A llist of the icons.
69
  """
70
 
71
- items = pathlib.Path(GlobalConfig.ICONS_DIR).glob('*.png')
72
- items = [
73
- os.path.basename(str(item)).removesuffix('.png') for item in items
74
- ]
75
-
76
- return items
77
 
78
 
79
  APP_TEXT = _load_strings()
 
3
  """
4
  import datetime
5
  import logging
 
6
  import pathlib
7
  import random
8
+ import sys
9
  import tempfile
10
  from typing import List, Union
11
 
 
15
  from langchain_core.messages import HumanMessage
16
  from langchain_core.prompts import ChatPromptTemplate
17
 
18
+ sys.path.append('..')
19
+ sys.path.append('../..')
20
+
21
+ import helpers.icons_embeddings as ice
22
  from global_config import GlobalConfig
23
  from helpers import llm_helper, pptx_helper, text_helper
24
 
 
72
  :return: A llist of the icons.
73
  """
74
 
75
+ return ice.get_icons_list()
 
 
 
 
 
76
 
77
 
78
  APP_TEXT = _load_strings()
file_embeddings/embeddings.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2f11da27237bdf979f27e6b7347e1ea0ac7a375f3492340871ee7f812709f5c9
3
+ size 95360
file_embeddings/icons.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:248012493bfb0d29c38a62a32204544e1a3eab9d0971e9c31d5d99216a8809c4
3
+ size 21704
global_config.py CHANGED
@@ -20,8 +20,8 @@ class GlobalConfig:
20
  HF_LLM_MODEL_NAME = 'mistralai/Mistral-Nemo-Instruct-2407'
21
  LLM_MODEL_TEMPERATURE = 0.2
22
  LLM_MODEL_MIN_OUTPUT_LENGTH = 100
23
- LLM_MODEL_MAX_OUTPUT_LENGTH = 4 * 4096
24
- LLM_MODEL_MAX_INPUT_LENGTH = 750
25
 
26
  HUGGINGFACEHUB_API_TOKEN = os.environ.get('HUGGINGFACEHUB_API_TOKEN', '')
27
  METAPHOR_API_KEY = os.environ.get('METAPHOR_API_KEY', '')
@@ -36,6 +36,9 @@ class GlobalConfig:
36
 
37
  LLM_PROGRESS_MAX = 90
38
  ICONS_DIR = 'icons/png128/'
 
 
 
39
 
40
  PPTX_TEMPLATE_FILES = {
41
  'Basic': {
 
20
  HF_LLM_MODEL_NAME = 'mistralai/Mistral-Nemo-Instruct-2407'
21
  LLM_MODEL_TEMPERATURE = 0.2
22
  LLM_MODEL_MIN_OUTPUT_LENGTH = 100
23
+ LLM_MODEL_MAX_OUTPUT_LENGTH = 4 * 4096 # tokens
24
+ LLM_MODEL_MAX_INPUT_LENGTH = 750 # characters
25
 
26
  HUGGINGFACEHUB_API_TOKEN = os.environ.get('HUGGINGFACEHUB_API_TOKEN', '')
27
  METAPHOR_API_KEY = os.environ.get('METAPHOR_API_KEY', '')
 
36
 
37
  LLM_PROGRESS_MAX = 90
38
  ICONS_DIR = 'icons/png128/'
39
+ TINY_BERT_MODEL = 'gaunernst/bert-tiny-uncased'
40
+ EMBEDDINGS_FILE_NAME = 'file_embeddings/embeddings.npy'
41
+ ICONS_FILE_NAME = 'file_embeddings/icons.npy'
42
 
43
  PPTX_TEMPLATE_FILES = {
44
  'Basic': {
helpers/icons_embeddings.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generate and save the embeddings of a pre-defined list of icons.
3
+ Compare them with keywords embeddings to find most relevant icons.
4
+ """
5
+ import os
6
+ import pathlib
7
+ import sys
8
+ from typing import List, Tuple
9
+
10
+ import numpy as np
11
+ from sklearn.metrics.pairwise import cosine_similarity
12
+ from transformers import BertTokenizer, BertModel
13
+
14
+ sys.path.append('..')
15
+ sys.path.append('../..')
16
+
17
+ from global_config import GlobalConfig
18
+
19
+
20
+ tokenizer = BertTokenizer.from_pretrained(GlobalConfig.TINY_BERT_MODEL)
21
+ model = BertModel.from_pretrained(GlobalConfig.TINY_BERT_MODEL)
22
+
23
+
24
+ def get_icons_list() -> List[str]:
25
+ """
26
+ Get a list of available icons.
27
+
28
+ :return: The icons file names.
29
+ """
30
+
31
+ items = pathlib.Path('../' + GlobalConfig.ICONS_DIR).glob('*.png')
32
+ items = [
33
+ os.path.basename(str(item)).removesuffix('.png') for item in items
34
+ ]
35
+
36
+ return items
37
+
38
+
39
+ def get_embeddings(texts) -> np.ndarray:
40
+ """
41
+ Generate embeddings for a list of texts using a pre-trained language model.
42
+
43
+ :param texts: A string or a list of strings to be converted into embeddings.
44
+ :type texts: Union[str, List[str]]
45
+ :return: A NumPy array containing the embeddings for the input texts.
46
+ :rtype: numpy.ndarray
47
+
48
+ :raises ValueError: If the input is not a string or a list of strings, or if any element
49
+ in the list is not a string.
50
+
51
+ Example usage:
52
+ >>> keyword = 'neural network'
53
+ >>> file_names = ['neural_network_icon.png', 'data_analysis_icon.png', 'machine_learning.png']
54
+ >>> keyword_embeddings = get_embeddings(keyword)
55
+ >>> file_name_embeddings = get_embeddings(file_names)
56
+ """
57
+
58
+ inputs = tokenizer(texts, return_tensors='pt', padding=True, max_length=128, truncation=True)
59
+ outputs = model(**inputs)
60
+
61
+ return outputs.last_hidden_state.mean(dim=1).detach().numpy()
62
+
63
+
64
+ def save_icons_embeddings():
65
+ """
66
+ Generate and save the embeddings for the icon file names.
67
+ """
68
+
69
+ file_names = get_icons_list()
70
+ file_name_embeddings = get_embeddings(file_names)
71
+
72
+ # Save embeddings to a file
73
+ np.save(GlobalConfig.EMBEDDINGS_FILE_NAME, file_name_embeddings)
74
+ np.save(GlobalConfig.ICONS_FILE_NAME, file_names) # Save file names for reference
75
+
76
+
77
+ def load_saved_embeddings() -> Tuple[np.ndarray, np.ndarray]:
78
+ """
79
+ Load precomputed embeddings and icons file names.
80
+
81
+ :return: The embeddings and the icon file names.
82
+ """
83
+
84
+ file_name_embeddings = np.load(GlobalConfig.EMBEDDINGS_FILE_NAME)
85
+ file_names = np.load(GlobalConfig.ICONS_FILE_NAME)
86
+
87
+ return file_name_embeddings, file_names
88
+
89
+
90
+ def find_icons(keywords: List[str]) -> List[str]:
91
+ """
92
+ Find relevant icon file names for a list of keywords.
93
+
94
+ :param keywords: The list of one or more keywords.
95
+ :return: A list of the file names relevant for each keyword.
96
+ """
97
+
98
+ keyword_embeddings = get_embeddings(keywords)
99
+ file_name_embeddings, file_names = load_saved_embeddings()
100
+
101
+ # Compute similarity
102
+ similarities = cosine_similarity(keyword_embeddings, file_name_embeddings)
103
+ icon_files = file_names[np.argmax(similarities, axis=-1)]
104
+
105
+ return icon_files
106
+
107
+
108
+ def main():
109
+ """
110
+ Example usage.
111
+ """
112
+
113
+ # Run this again if icons are to be added/removed
114
+ # save_icons_embeddings()
115
+
116
+ keywords = ['deep learning', 'library', 'universe', 'brain', 'cybersecurity', 'gaming', '']
117
+ icon_files = find_icons(keywords)
118
+ print(f'The relevant icon files are: {icon_files}')
119
+
120
+
121
+ if __name__ == '__main__':
122
+ main()
helpers/pptx_helper.py CHANGED
@@ -19,6 +19,7 @@ from pptx.shapes.placeholder import PicturePlaceholder, SlidePlaceholder
19
  sys.path.append('..')
20
  sys.path.append('../..')
21
 
 
22
  import helpers.image_search as ims
23
  from global_config import GlobalConfig
24
 
@@ -493,24 +494,28 @@ def _handle_icons_ideas(
493
 
494
  # Calculate the total width of all pictures and the spacing
495
  total_width = n_items * ICON_SIZE
496
- # slide_width = presentation.slide_width
497
  spacing = (pptx.util.Inches(slide_width_inch) - total_width) / (n_items + 1)
498
-
499
- for idx, item in enumerate(items):
500
- # Extract the icon name and text
501
- match = ICONS_REGEX.search(item)
502
-
503
- if not match:
504
- # print('No icon/text pattern match found...skipping to the next item')
505
- continue
506
-
507
- icon_name = match.group(1)
508
- accompanying_text = match.group(2)
509
- icon_path = f'{GlobalConfig.ICONS_DIR}/{icon_name}.png'
 
 
 
 
 
 
 
510
 
511
  left = spacing + idx * (ICON_SIZE + spacing)
512
- top = INCHES_3
513
-
514
  # Calculate the center position for alignment
515
  center = left + ICON_SIZE / 2
516
 
@@ -526,30 +531,18 @@ def _handle_icons_ideas(
526
  shape.shadow.inherit = False
527
 
528
  # Set the icon's background shape color
529
- color = random.choice(ICON_COLORS)
530
- shape.fill.fore_color.rgb = color
531
- shape.line.color.rgb = color
532
 
533
  # Add the icon image on top of the colored shape
534
- try:
535
- slide.shapes.add_picture(icon_path, left, top, height=ICON_SIZE)
536
- except FileNotFoundError:
537
- logger.error(
538
- 'Icon %s not found...using generic step number as icon...',
539
- icon_name
540
- )
541
- step_icon_path = f'{GlobalConfig.ICONS_DIR}/{idx + 1}-circle.png'
542
- if os.path.exists(step_icon_path):
543
- slide.shapes.add_picture(step_icon_path, left, top, height=ICON_SIZE)
544
 
545
  # Add a text box below the shape
546
- text_top = top + ICON_SIZE + INCHES_0_2
547
- text_left = center - text_box_size / 2 # Center the text box horizontally
548
- # text_box = slide.shapes.add_textbox(text_left, text_top, text_box_size, text_box_size)
549
  text_box = slide.shapes.add_shape(
550
  MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE,
551
- text_left, text_top,
552
- text_box_size, text_box_size
 
 
553
  )
554
  text_frame = text_box.text_frame
555
  text_frame.text = accompanying_text
@@ -955,7 +948,7 @@ if __name__ == '__main__':
955
  "bullet_points": [
956
  "[[brain]] Human-like intelligence and decision-making",
957
  "[[robot]] Automation and physical tasks",
958
- "[[cloud]] Data processing and cloud computing",
959
  "[[lightbulb]] Insights and predictions",
960
  "[[globe2]] Global connectivity and impact"
961
  ],
 
19
  sys.path.append('..')
20
  sys.path.append('../..')
21
 
22
+ import helpers.icons_embeddings as ice
23
  import helpers.image_search as ims
24
  from global_config import GlobalConfig
25
 
 
494
 
495
  # Calculate the total width of all pictures and the spacing
496
  total_width = n_items * ICON_SIZE
 
497
  spacing = (pptx.util.Inches(slide_width_inch) - total_width) / (n_items + 1)
498
+ top = INCHES_3
499
+
500
+ icons_texts = [
501
+ (match.group(1), match.group(2)) for match in [
502
+ ICONS_REGEX.search(item) for item in items
503
+ ]
504
+ ]
505
+ fallback_icon_files = ice.find_icons([item[0] for item in icons_texts])
506
+
507
+ for idx, item in enumerate(icons_texts):
508
+ icon, accompanying_text = item
509
+ icon_path = f'{GlobalConfig.ICONS_DIR}/{icon}.png'
510
+
511
+ if not os.path.exists(icon_path):
512
+ logger.warning(
513
+ 'Icon not found: %s...using fallback icon: %s',
514
+ icon, fallback_icon_files[idx]
515
+ )
516
+ icon_path = f'{GlobalConfig.ICONS_DIR}/{fallback_icon_files[idx]}.png'
517
 
518
  left = spacing + idx * (ICON_SIZE + spacing)
 
 
519
  # Calculate the center position for alignment
520
  center = left + ICON_SIZE / 2
521
 
 
531
  shape.shadow.inherit = False
532
 
533
  # Set the icon's background shape color
534
+ shape.fill.fore_color.rgb = shape.line.color.rgb = random.choice(ICON_COLORS)
 
 
535
 
536
  # Add the icon image on top of the colored shape
537
+ slide.shapes.add_picture(icon_path, left, top, height=ICON_SIZE)
 
 
 
 
 
 
 
 
 
538
 
539
  # Add a text box below the shape
 
 
 
540
  text_box = slide.shapes.add_shape(
541
  MSO_AUTO_SHAPE_TYPE.ROUNDED_RECTANGLE,
542
+ left=center - text_box_size / 2, # Center the text box horizontally
543
+ top=top + ICON_SIZE + INCHES_0_2,
544
+ width=text_box_size,
545
+ height=text_box_size
546
  )
547
  text_frame = text_box.text_frame
548
  text_frame.text = accompanying_text
 
948
  "bullet_points": [
949
  "[[brain]] Human-like intelligence and decision-making",
950
  "[[robot]] Automation and physical tasks",
951
+ "[[]] Data processing and cloud computing",
952
  "[[lightbulb]] Insights and predictions",
953
  "[[globe2]] Global connectivity and impact"
954
  ],
langchain_templates/chat_prompts/initial_template_v4_two_cols_img.txt CHANGED
@@ -13,22 +13,18 @@ In addition, for each slide, add image keywords based on the content of the resp
13
  These keywords will be later used to search for images from the Web relevant to the slide content.
14
 
15
  In addition, create one slide containing 4 TO 6 icons (pictograms) illustrating some key ideas/aspects/concepts relevant to the topic.
16
- In this slide, each line of text will begin with the name of an icon enclosed between [[ and ]].
17
- The name of an icon MUST BE chosen exactly as it is from the <ICONS> section provided below and should be relevant to the aspects described.
18
- If you need an icon that is unavailable in the <ICONS> section, you may select a conceptually similar icon's name from <ICONS>.
19
- For example, if an icon for "neural network" is unavailable, you may choose the "deep-learning" icon from <ICONS>.
20
- However, you MUST NEVER generate any icon name not mentioned in the <ICONS> section.
21
-
22
- The content of each slide should be VERBOSE, DESCRIPTIVE, and very DETAILED.
23
-
24
- ALWAYS add a concluding slide at the end, containing a list of the key takeaways and an optional call-to-action if relevant to the context.
25
- Unless explicitly instructed, create 10 TO 12 SLIDES in total.
26
-
27
 
28
  <ICONS>
29
  {icons_list}
30
  </ICONS>
31
 
 
 
 
 
 
32
 
33
  ### Topic:
34
  {question}
@@ -64,10 +60,10 @@ The output must be only a valid and syntactically correct JSON adhering to the f
64
  {{
65
  "heading": "A slide illustrating key ideas/aspects/concepts (Hint: generate an appropriate heading)",
66
  "bullet_points": [
67
- "[[icon_name]] Some text",
68
- "[[another_icon_name]] Some words describing this aspect",
69
- "[[icon_other_name]] Another aspect highlighted here",
70
- "[[an_icon]] Another point here",
71
  ],
72
  "key_message": "",
73
  "img_keywords": ""
 
13
  These keywords will be later used to search for images from the Web relevant to the slide content.
14
 
15
  In addition, create one slide containing 4 TO 6 icons (pictograms) illustrating some key ideas/aspects/concepts relevant to the topic.
16
+ In this slide, each line of text will begin with the name of a relevant icon enclosed between [[ and ]].
17
+ Select appropriate and exact icon names from the <ICONS> section provided below.
 
 
 
 
 
 
 
 
 
18
 
19
  <ICONS>
20
  {icons_list}
21
  </ICONS>
22
 
23
+ The content of each slide should be VERBOSE, DESCRIPTIVE, and very DETAILED. Each bullet point should be detailed and explanatory, not just short phrases.
24
+
25
+ ALWAYS add a concluding slide at the end, containing a list of the key takeaways and an optional call-to-action if relevant to the context.
26
+ Unless explicitly instructed with the topic, create 10 TO 12 SLIDES in total. You must never create more tha 15 slides.
27
+
28
 
29
  ### Topic:
30
  {question}
 
60
  {{
61
  "heading": "A slide illustrating key ideas/aspects/concepts (Hint: generate an appropriate heading)",
62
  "bullet_points": [
63
+ "[[icon name]] Some text",
64
+ "[[another icon name]] Some words describing this aspect",
65
+ "[[icon]] Another aspect highlighted here",
66
+ "[[an icon]] Another point here",
67
  ],
68
  "key_message": "",
69
  "img_keywords": ""
langchain_templates/chat_prompts/refinement_template_v4_two_cols_img.txt CHANGED
@@ -14,21 +14,17 @@ In addition, for each slide, add image keywords based on the content of the resp
14
  These keywords will be later used to search for images from the Web relevant to the slide content.
15
 
16
  In addition, create one slide containing 4 TO 6 icons (pictograms) illustrating some key ideas/aspects/concepts relevant to the topic.
17
- In this slide, each line of text will begin with the name of an icon enclosed between [[ and ]].
18
- The name of an icon MUST BE chosen exactly as it is from the <ICONS> section provided below and should be relevant to the aspects described.
19
- If you need an icon that is unavailable in the <ICONS> section, you may select a conceptually similar icon's name from <ICONS>.
20
- For example, if an icon for "neural network" is unavailable, you may choose the "deep-learning" icon from <ICONS>.
21
- However, you MUST NEVER generate any icon name not mentioned in the <ICONS> section.
22
 
23
- The content of each slide should be VERBOSE, DESCRIPTIVE, and very DETAILED.
24
-
25
- ALWAYS add a concluding slide at the end, containing a list of the key takeaways and an optional call-to-action if relevant to the context.
26
- Unless explicitly instructed, create 10 TO 12 SLIDES in total.
27
 
 
28
 
29
- <Icons>
30
- {icons_list}
31
- </Icons>
32
 
33
 
34
  ### List of instructions:
@@ -69,10 +65,10 @@ The output must be only a valid and syntactically correct JSON adhering to the f
69
  {{
70
  "heading": "A slide illustrating key ideas/aspects/concepts (Hint: generate an appropriate heading)",
71
  "bullet_points": [
72
- "[[icon_name]] Some text",
73
- "[[another_icon_name]] Some words describing this aspect",
74
- "[[icon_other_name]] Another aspect highlighted here",
75
- "[[an_icon]] Another point here",
76
  ],
77
  "key_message": "",
78
  "img_keywords": ""
 
14
  These keywords will be later used to search for images from the Web relevant to the slide content.
15
 
16
  In addition, create one slide containing 4 TO 6 icons (pictograms) illustrating some key ideas/aspects/concepts relevant to the topic.
17
+ In this slide, each line of text will begin with the name of a relevant icon enclosed between [[ and ]].
18
+ Select appropriate and exact icon names from the <ICONS> section provided below.
 
 
 
19
 
20
+ <ICONS>
21
+ {icons_list}
22
+ </ICONS>
 
23
 
24
+ The content of each slide should be VERBOSE, DESCRIPTIVE, and very DETAILED. Each bullet point should be detailed and explanatory, not just short phrases.
25
 
26
+ ALWAYS add a concluding slide at the end, containing a list of the key takeaways and an optional call-to-action if relevant to the context.
27
+ Unless explicitly specified in the instructions below, create 10 TO 12 SLIDES in total. You must never create more tha 15 slides.
 
28
 
29
 
30
  ### List of instructions:
 
65
  {{
66
  "heading": "A slide illustrating key ideas/aspects/concepts (Hint: generate an appropriate heading)",
67
  "bullet_points": [
68
+ "[[icon name]] Some text",
69
+ "[[another icon name]] Some words describing this aspect",
70
+ "[[icon]] Another aspect highlighted here",
71
+ "[[an icon]] Another point here",
72
  ],
73
  "key_message": "",
74
  "img_keywords": ""
requirements.txt CHANGED
@@ -16,9 +16,11 @@ metaphor-python
16
  json5~=0.9.14
17
  requests~=2.31.0
18
 
19
- transformers~=4.39.2
20
  langchain-community
21
 
22
  urllib3~=2.2.1
23
  lxml~=4.9.3
24
- tqdm~=4.64.1
 
 
 
16
  json5~=0.9.14
17
  requests~=2.31.0
18
 
19
+ transformers~=4.44.0
20
  langchain-community
21
 
22
  urllib3~=2.2.1
23
  lxml~=4.9.3
24
+ tqdm~=4.64.1
25
+ numpy~=1.25.2
26
+ scikit-learn~=1.5.1