tmzh commited on
Commit
a293444
·
1 Parent(s): cb05500

initial commit

Browse files
Files changed (1) hide show
  1. app.py +232 -0
app.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import random
4
+
5
+ import google.generativeai as genai
6
+ import gradio as gr
7
+ from jinja2 import Environment, FileSystemLoader
8
+
9
+ GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
10
+
11
+ genai.configure(api_key=GOOGLE_API_KEY)
12
+
13
+ model = genai.GenerativeModel("gemini-1.5-flash-latest",
14
+ generation_config={"response_mime_type": "application/json"})
15
+
16
+
17
+ def merge_games(clues, num_merges=10):
18
+ """Generates around 10 merges of words from the given clues.
19
+
20
+ Args:
21
+ clues: A list of clues, where each clue is a list containing the words, the answer, and the explanation.
22
+ num_merges: The approximate number of merges to generate (default: 10).
23
+
24
+ Returns:
25
+ A list of tuples, where each tuple contains the merged words and the indices of the selected rows.
26
+ """
27
+
28
+ merged = []
29
+ while len(merged) < num_merges:
30
+ num_rows = random.choice([3, 4])
31
+ selected_rows = random.sample(range(len(clues)), num_rows)
32
+ words_list = [word for row in [clues[i][0] for i in selected_rows] for word in row]
33
+ if len(words_list) in [8, 9]:
34
+ merged.append((words_list, selected_rows))
35
+
36
+ return merged
37
+
38
+
39
+ example_clues = [
40
+ (['ARROW', 'TIE', 'HONOR'], 'BOW', 'such as a bow and arrow, a bow tie, or a bow as a sign of honor'),
41
+ (['DOG', 'TREE'], 'BARK', 'such as the sound a dog makes, or a tree is made of bark'),
42
+ (['MONEY', 'RIVER', 'ROB', 'BLOOD'], 'CRIME', 'such as money being stolen, a river being a potential crime scene, '
43
+ 'robbery, or blood being a result of a violent crime'),
44
+ (['BEEF', 'TURKEY', 'FIELD', 'GRASS'], 'GROUND',
45
+ 'such as ground beef, a turkey being a ground-dwelling bird, a field or grass being a type of ground'),
46
+ (['BANK', 'GUITAR', 'LIBRARY'], 'NOTE',
47
+ 'such as a bank note, a musical note on a guitar, or a note being a written comment in a library book'),
48
+ (['ROOM', 'PIANO', 'TYPEWRITER'], 'KEYS', 'such as a room key, piano keys, or typewriter keys'),
49
+ (['TRAFFIC', 'RADAR', 'PHONE'], 'SIGNAL', 'such as traffic signals, radar signals, or phone signals'),
50
+ (['FENCE', 'PICTURE', 'COOKIE'], 'FRAME',
51
+ 'such as a frame around a yard, a picture frame, or a cookie cutter being a type of frame'),
52
+ (['YARN', 'VIOLIN', 'DRESS'], 'STRING', 'strings like material, instrument, clothing fastener'),
53
+ (['JUMP', 'FLOWER', 'CLOCK'], 'SPRING',
54
+ 'such as jumping, flowers blooming in the spring, or a clock having a sprint component'),
55
+ (['SPY', 'KNIFE'], 'WAR',
56
+ 'Both relate to aspects of war, such as spies being involved in war or knives being used as weapons'),
57
+ (['STADIUM', 'SHOE', 'FIELD'], 'SPORT', 'Sports like venues, equipment, playing surfaces'),
58
+ (['TEACHER', 'CLUB'], 'SCHOOL',
59
+ 'such as a teacher being a school staff member or a club being a type of school organization'),
60
+ (['CYCLE', 'ARMY', 'COURT', 'FEES'], 'CHARGE', 'charges like electricity, battle, legal, payments'),
61
+ (['FRUIT', 'MUSIC', 'TRAFFIC', 'STUCK'], 'JAM',
62
+ 'Jams such as fruit jam, a music jam session, traffic jam, or being stuck in a jam'),
63
+ (['POLICE', 'DOG', 'THIEF'], 'CRIME',
64
+ 'such as police investigating crimes, dogs being used to detect crimes, or a thief committing a crime'),
65
+ (['ARCTIC', 'SHUT', 'STAMP'], 'SEAL',
66
+ 'such as the Arctic being home to seals, or shutting a seal on an envelope, or a stamp being a type of seal'),
67
+ ]
68
+
69
+ example_groupings = []
70
+ merges = merge_games(example_clues, 5)
71
+ for merged_words, indices in merges:
72
+ groups = [{
73
+ "words": example_clues[i][0],
74
+ "clue": example_clues[i][1],
75
+ "explanation": example_clues[i][2]
76
+ } for i in indices]
77
+ example_groupings.append((merged_words, json.dumps(groups, separators=(',', ':'))))
78
+
79
+
80
+ def render_jinja2_template(template, system_prompt, history, query):
81
+ env = Environment(loader=FileSystemLoader('.'))
82
+ template = env.from_string(template)
83
+ return template.render(system_prompt=system_prompt, history=history, query=query)
84
+
85
+
86
+ def group_words(words):
87
+ template = '''
88
+ {% for example in history %}
89
+ INPUT:
90
+ {{ example[0] }}
91
+ OUTPUT:
92
+ {{ example[1] }}
93
+ {% endfor %}
94
+ INPUT:
95
+ {{ query }}
96
+ OUTPUT:
97
+
98
+ {{ system }}
99
+
100
+ Groups = {'words': list[str], 'clue': str, 'explanation': str}
101
+ Return: Groups
102
+ '''
103
+
104
+ grouping_system_prompt = ("You are an assistant for the game Codenames. Your task is to help players by grouping a "
105
+ "given group of secrets into 3 to 4 groups. Each group should consist of secrets that "
106
+ "share a common theme or other word connections such as homonym, hypernyms or synonyms")
107
+
108
+ prompt = render_jinja2_template(template, grouping_system_prompt, example_groupings, words)
109
+ # print(prompt)
110
+ raw_response = model.generate_content(
111
+ prompt,
112
+ generation_config={'top_k': 3, 'temperature': 1.1})
113
+ response = json.loads(raw_response.text)
114
+
115
+ print("Grouping words:", words)
116
+
117
+ print("Got groupings: ", json.dumps(response, indent=4))
118
+ return [group["words"] for group in response]
119
+
120
+
121
+ def generate_clues(group):
122
+ template = '''
123
+ {% for example in history %}
124
+ INPUT:
125
+ {{ example[0] }}
126
+ OUTPUT:
127
+ { 'clue':{{ example[1] }}, 'explanation':{{ example[2] }} }
128
+ {% endfor %}
129
+ INPUT:
130
+ {{ query }}
131
+ OUTPUT:
132
+
133
+
134
+ {{ system }}
135
+
136
+ Clue = {'clue': str, 'explanation': str}
137
+ Return: Clue
138
+ '''
139
+
140
+ clue_system_prompt = ("You are a codenames game companion. Your task is to give a single word clue related to "
141
+ "a given group of words. You will only respond with a single word clue.")
142
+
143
+ prompt = render_jinja2_template(template, clue_system_prompt, example_clues, group)
144
+
145
+ raw_response = model.generate_content(
146
+ prompt,
147
+ generation_config={'top_k': 3, 'temperature': 1.1})
148
+ response = json.loads(raw_response.text)
149
+
150
+ print("Generating clues for: ", group)
151
+
152
+ print("Got clue: ", json.dumps(response, indent=4))
153
+ return response
154
+
155
+
156
+ def process_image(img):
157
+ print(img, type(img))
158
+ raw_response = model.generate_content(['Identify the words in this game of Codenames. Provide only a list of '
159
+ 'words. Provide the words in capital letters only. Group these words into '
160
+ '6 or 8 groups that can be guessed together using a single word clue in '
161
+ 'the game of codenames. Give a response as json of the form: {"Game": '
162
+ '<list of words in the game>}', img], stream=True)
163
+ raw_response.resolve()
164
+ response = json.loads(raw_response.text)
165
+ words = response['Game']
166
+ return gr.update(choices=words, value=words)
167
+
168
+
169
+ with gr.Blocks() as demo:
170
+ gr.Markdown("# *Codenames* clue generator")
171
+ gr.Markdown("Provide a list of words to generate a clue")
172
+
173
+ with gr.Row():
174
+ game_image = gr.Image(type="pil")
175
+ word_list_input = gr.Dropdown(label="Enter list of words (comma separated)",
176
+ choices='WEREWOLF, CHAIN, MOSQUITO, CRAFT, RANCH, LIP, VALENTINE, CLOUD, '
177
+ 'BEARD, BUNK, SECOND, SADDLE, BUCKET, JAIL, ANT, POCKET, LACE, '
178
+ 'BREAK, CUCKOO, FLAT, NIL, TIN, CHERRY, CHRISTMAS, MOSES, '
179
+ 'TEAM'.split(', '),
180
+ multiselect=True,
181
+ interactive=True)
182
+
183
+ with gr.Row():
184
+ detect_words_button = gr.Button("Detect Words")
185
+ group_words_button = gr.Button("Group Words")
186
+
187
+ dropdowns, buttons, outputs = [], [], []
188
+
189
+ for i in range(4):
190
+ with gr.Row():
191
+ group_input = gr.Dropdown(label=f"Group {i + 1}",
192
+ choices=[],
193
+ allow_custom_value=True,
194
+ multiselect=True,
195
+ interactive=True)
196
+ clue_button = gr.Button("Generate Clue", size='sm')
197
+ clue_output = gr.Textbox(label=f"Clue {i + 1}")
198
+ dropdowns.append(group_input)
199
+ buttons.append(clue_button)
200
+ outputs.append(clue_output)
201
+
202
+
203
+ def pad_or_truncate(lst, n=4):
204
+ # Ensure the length of the list is at most n
205
+ truncated_lst = lst[:n]
206
+ return truncated_lst + (n - len(truncated_lst)) * [None]
207
+
208
+
209
+ def group_words_callback(words):
210
+ word_groups = group_words(words)
211
+ word_groups = pad_or_truncate(word_groups, 4)
212
+ print("Got groups: ", word_groups, type(word_groups))
213
+ return [gr.update(value=word_groups[i], choices=words) for i in range(4)]
214
+
215
+
216
+ def generate_clues_callback(group):
217
+ print("Generating clues: ", group)
218
+ g = generate_clues(group)
219
+ return gr.update(value=g['clue'], info=g['explanation'])
220
+
221
+
222
+ detect_words_button.click(fn=process_image,
223
+ inputs=game_image,
224
+ outputs=[word_list_input])
225
+ group_words_button.click(fn=group_words_callback,
226
+ inputs=word_list_input,
227
+ outputs=dropdowns)
228
+
229
+ for i in range(4):
230
+ buttons[i].click(generate_clues_callback, inputs=dropdowns[i], outputs=outputs[i])
231
+
232
+ demo.launch(share=True, debug=True)