HMC83 commited on
Commit
6cc1ea1
·
verified ·
1 Parent(s): b86363b

initial commit

Browse files
Files changed (1) hide show
  1. app.py +354 -0
app.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import random
4
+ import time
5
+ import torch
6
+ from transformers import AutoTokenizer, AutoModelForCausalLM
7
+ import spaces
8
+
9
+ MODEL_ID = "HMC83/request_badger_olmo"
10
+
11
+ # --- Load Model and Tokenizer ---
12
+ print("Loading model and tokenizer...")
13
+ try:
14
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
15
+ model = AutoModelForCausalLM.from_pretrained(
16
+ MODEL_ID,
17
+ torch_dtype=torch.bfloat16,
18
+ device_map="auto"
19
+ )
20
+ print("Model loaded successfully.")
21
+ except Exception as e:
22
+ print(f"Error loading model: {e}")
23
+ model = None
24
+ tokenizer = None
25
+
26
+ # --- Data for the Reels ---
27
+ # A list of authority and keyword combinations.
28
+ FOI_COMBINATIONS = [
29
+ {"authority": "Borders NHS Board", "keywords": "whistleblowing guidance, wrongdoing, public body"},
30
+ {"authority": "Borders NHS Board", "keywords": "ethical support, clinical triage, minutes"},
31
+ {"authority": "Buckinghamshire Fire and Rescue Service", "keywords": "fire service, fire officers, membership"},
32
+ {"authority": "Ballymoney Borough Council", "keywords": "parking enforcement, employee responsibility, parking services"},
33
+ {"authority": "British Council", "keywords": "British Council funding, festival, correspondence"},
34
+ {"authority": "Cardiff and Vale University Health Board", "keywords": "drugs, therapeutic, meetings"},
35
+ {"authority": "Hastings Borough Council", "keywords": "local authority care, protection orders, adopted children"},
36
+ {"authority": "Sevenoaks District Council", "keywords": "residential property, council tax, postcode"},
37
+ {"authority": "The Marine Society and Sea Cadets", "keywords": "training, sea cadets, years in office"},
38
+ {"authority": "West Yorkshire Police Authority", "keywords": "professional standards, official duties, chief constable"},
39
+ {"authority": "Royal College of Veterinary Surgeons", "keywords": "veterinary practices, veterinary hospitals, contact details"},
40
+ {"authority": "South Tyneside Metropolitan Borough Council", "keywords": "commercial customers, waste collection, waste producers"},
41
+ {"authority": "University of Surrey", "keywords": "university car parks, parking tickets, parking enforcement"},
42
+ {"authority": "King's College Hospital NHS Trust", "keywords": "annual spend, medical equipment, procurement"},
43
+ {"authority": "Great Ormond Street Hospital", "keywords": "charity donations, research funding, financial year"},
44
+ {"authority": "Public Health England", "keywords": "vaccination costs, public health campaigns, effectiveness"},
45
+ {"authority": "NHS Digital", "keywords": "IT spending, system outages, cyber security"},
46
+ {"authority": "Cornwall Council", "keywords": "adult social care, spending, assessments"},
47
+ {"authority": "Hertfordshire County Council", "keywords": "school transport, route costs, contractor payments"},
48
+ {"authority": "Wiltshire Council", "keywords": "highway maintenance, pothole repairs, annual spend"},
49
+ {"authority": "Surrey County Council", "keywords": "council tax, collection rates, arrears"},
50
+ {"authority": "Ofgem", "keywords": "energy company, penalties, investigations"},
51
+ {"authority": "Financial Conduct Authority", "keywords": "enforcement action, fines imposed, complaint statistics"},
52
+ {"authority": "Competition and Markets Authority", "keywords": "merger investigations, market studies, legal costs"},
53
+ {"authority": "Health and Safety Executive", "keywords": "workplace accidents, enforcement notices, prosecution costs"},
54
+ {"authority": "Network Rail", "keywords": "delay compensation, signal failures, maintenance costs"},
55
+ {"authority": "Highways England", "keywords": "road maintenance, contractor spending, traffic flow"},
56
+ {"authority": "Civil Aviation Authority", "keywords": "airline complaints, enforcement action, safety investigations"},
57
+ {"authority": "HM Revenue and Customs", "keywords": "tax investigations, recovery rates, compliance costs"},
58
+ {"authority": "Department for Work and Pensions", "keywords": "benefit sanctions, appeal outcomes, administrative costs"},
59
+ {"authority": "Nottinghamshire County Council", "keywords": "adult social care, budget allocation, waiting lists"},
60
+ {"authority": "Staffordshire County Council", "keywords": "school meals, contract costs, nutritional standards"},
61
+ {"authority": "Derbyshire County Council", "keywords": "library services, closure consultations, usage statistics"},
62
+ {"authority": "East Sussex County Council", "keywords": "children services, safeguarding, staffing costs"},
63
+ {"authority": "Northumberland County Council", "keywords": "waste collection, recycling rates, contractor performance"},
64
+ {"authority": "Royal Devon NHS Trust", "keywords": "patient complaints, response times, resolution outcomes"},
65
+ {"authority": "Sheffield Teaching Hospitals", "keywords": "medical equipment, procurement costs, maintenance contracts"},
66
+ {"authority": "Bradford Teaching Hospitals", "keywords": "staff training, external courses, annual expenditure"},
67
+ {"authority": "Northampton General Hospital", "keywords": "parking charges, revenue generated, patient feedback"},
68
+ {"authority": "Environment Agency", "keywords": "flood defenses, maintenance costs, effectiveness assessments"},
69
+ {"authority": "Natural England", "keywords": "habitat protection, designation costs, landowner compensation"},
70
+ {"authority": "Marine Management Organisation", "keywords": "fishing licenses, enforcement costs, violation penalties"},
71
+ {"authority": "Forestry Commission", "keywords": "timber sales, revenue targets, environmental impact"},
72
+ {"authority": "Welsh Government", "keywords": "language services, translation costs, usage statistics"},
73
+ {"authority": "Scottish Government", "keywords": "ferry subsidies, route profitability, passenger numbers"},
74
+ {"authority": "Northern Ireland Executive", "keywords": "cross-border initiatives, funding allocation, outcomes"},
75
+ {"authority": "Office for National Statistics", "keywords": "census costs, data collection, contractor payments"},
76
+ {"authority": "Land Registry", "keywords": "property registrations, processing times, fee income"},
77
+ {"authority": "Companies House", "keywords": "late filing penalties, collection rates, enforcement action"},
78
+ {"authority": "Intellectual Property Office", "keywords": "patent applications, processing costs, appeal outcomes"},
79
+ {"authority": "InGen Safety Authority", "keywords": "containment breaches, insurance costs, visitor incidents"},
80
+ {"authority": "Natural England", "keywords": "Bigfoot sightings, habitat, evidence"},
81
+ {"authority": "The Royal Parks", "keywords": "squirrel uprising, acorn stockpiles, intelligence"},
82
+ {"authority": "Midsomer Constabulary", "keywords": "unusually high murder rate, statistics, investigations"},
83
+ {"authority": "Scottish Government", "keywords": "Loch Ness Monster, research, funding"},
84
+ {"authority": "Westminster City Council", "keywords": "noise complaints, nightlife, enforcement"},
85
+ {"authority": "Network Rail", "keywords": "delays, compensation, signal failures"},
86
+ {"authority": "Gloucestershire County Council", "keywords": "adult social care, assessment costs, service provision"},
87
+ {"authority": "Worcestershire County Council", "keywords": "school transport, route planning, contractor performance"},
88
+ {"authority": "Leicestershire County Council", "keywords": "highway maintenance, annual spend, emergency repairs"},
89
+ {"authority": "Warwickshire County Council", "keywords": "library services, usage statistics, closure consultations"},
90
+ {"authority": "Nottinghamshire County Council", "keywords": "children services, safeguarding referrals, staff costs"},
91
+ {"authority": "Derbyshire County Council", "keywords": "waste disposal, recycling rates, collection contracts"},
92
+ {"authority": "Staffordshire County Council", "keywords": "care homes, inspection reports, quality ratings"},
93
+ {"authority": "Shropshire Council", "keywords": "planning applications, approval rates, enforcement action"},
94
+ {"authority": "Herefordshire Council", "keywords": "council tax, collection rates, arrears management"},
95
+ {"authority": "Cheshire East Council", "keywords": "parking enforcement, penalty notices, appeal outcomes"},
96
+ {"authority": "Royal Liverpool University Hospital", "keywords": "patient complaints, response times, resolution outcomes"},
97
+ {"authority": "Sheffield Teaching Hospitals NHS Trust", "keywords": "medical equipment, procurement costs, maintenance contracts"},
98
+ {"authority": "Leeds Teaching Hospitals NHS Trust", "keywords": "agency staff, temporary costs, recruitment spend"},
99
+ {"authority": "Newcastle upon Tyne Hospitals NHS Trust", "keywords": "cancelled operations, reasons given, patient impact"},
100
+ {"authority": "University Hospitals Birmingham", "keywords": "waiting times, treatment delays, performance targets"},
101
+ {"authority": "Guy's and St Thomas' NHS Trust", "keywords": "infection control, outbreak incidents, prevention costs"},
102
+ {"authority": "King's College Hospital NHS Trust", "keywords": "emergency admissions, capacity management, staffing levels"},
103
+ {"authority": "Royal Free NHS Trust", "keywords": "clinical trials, research funding, patient consent"},
104
+ {"authority": "Imperial College Healthcare NHS Trust", "keywords": "private patient, income generated, capacity allocation"},
105
+ {"authority": "Barts Health NHS Trust", "keywords": "financial deficit, recovery plans, cost reduction"},
106
+ {"authority": "West Yorkshire Police", "keywords": "response times, emergency calls, performance standards"},
107
+ {"authority": "South Yorkshire Police", "keywords": "crime statistics, detection rates, resource allocation"},
108
+ {"authority": "North Yorkshire Police", "keywords": "rural policing, coverage areas, response costs"},
109
+ {"authority": "Humberside Police", "keywords": "traffic enforcement, speed cameras, revenue generated"},
110
+ {"authority": "Lincolnshire Police", "keywords": "domestic violence, incident reports, support services"},
111
+ {"authority": "Nottinghamshire Police", "keywords": "stop and search, statistics recorded, outcome data"},
112
+ {"authority": "Derbyshire Police", "keywords": "drug seizures, investigation costs, conviction rates"},
113
+ {"authority": "Leicestershire Police", "keywords": "neighbourhood policing, community engagement, effectiveness measures"},
114
+ {"authority": "Warwickshire Police", "keywords": "police stations, closure plans, public consultations"},
115
+ {"authority": "West Mercia Police", "keywords": "cybercrime, investigation resources, training costs"},
116
+ {"authority": "Environment Agency", "keywords": "flood defenses, maintenance spending, effectiveness assessments"},
117
+ {"authority": "Natural England", "keywords": "habitat designations, compensation payments, landowner agreements"},
118
+ {"authority": "Historic England", "keywords": "listed buildings, consent applications, enforcement cases"},
119
+ {"authority": "Highways England", "keywords": "motorway maintenance, contractor payments, performance indicators"},
120
+ {"authority": "Network Rail", "keywords": "signal failures, delay minutes, compensation costs"},
121
+ {"authority": "Civil Aviation Authority", "keywords": "airline complaints, resolution times, enforcement action"},
122
+ {"authority": "Maritime and Coastguard Agency", "keywords": "rescue operations, helicopter costs, response times"},
123
+ {"authority": "Driver and Vehicle Standards Agency", "keywords": "MOT failures, test center, compliance rates"},
124
+ {"authority": "Vehicle Certification Agency", "keywords": "emissions testing, approval procedures, manufacturer compliance"},
125
+ {"authority": "Planning Inspectorate", "keywords": "appeal decisions, processing times, outcome statistics"},
126
+ {"authority": "Health and Safety Executive", "keywords": "workplace accidents, investigation costs, prosecution outcomes"},
127
+ {"authority": "Care Quality Commission", "keywords": "inspection reports, rating changes, enforcement action"},
128
+ {"authority": "Ofgem", "keywords": "energy company, penalties imposed, consumer complaints"},
129
+ {"authority": "Ofwat", "keywords": "water companies, price reviews, performance monitoring"},
130
+ {"authority": "Ofcom", "keywords": "broadcasting complaints, investigation procedures, penalty decisions"},
131
+ {"authority": "Financial Conduct Authority", "keywords": "financial services, enforcement cases, penalty amounts"},
132
+ {"authority": "Competition and Markets Authority", "keywords": "merger investigations, market studies, intervention costs"},
133
+ {"authority": "Gambling Commission", "keywords": "license suspensions, operator penalties, compliance monitoring"},
134
+ {"authority": "Information Commissioner's Office", "keywords": "data breaches, penalty notices, investigation outcomes"},
135
+ {"authority": "Electoral Commission", "keywords": "campaign spending, compliance checks, penalty procedures"},
136
+ {"authority": "HM Revenue and Customs", "keywords": "tax investigations, recovery amounts, compliance costs"},
137
+ {"authority": "HM Treasury", "keywords": "departmental budgets, spending reviews, efficiency targets"},
138
+ {"authority": "Cabinet Office", "keywords": "government consultancy, external advisors, procurement costs"},
139
+ {"authority": "Ministry of Justice", "keywords": "court delays, case backlogs, administrative costs"},
140
+ {"authority": "Home Office", "keywords": "immigration appeals, processing times, detention costs"},
141
+ {"authority": "Department for Transport", "keywords": "railway subsidies, franchise performance, punctuality targets"},
142
+ {"authority": "Department for Education", "keywords": "academy conversions, funding allocations, performance data"},
143
+ {"authority": "Department for Work and Pensions", "keywords": "benefit sanctions, appeal success, administrative costs"},
144
+ {"authority": "Department of Health", "keywords": "NHS funding, allocation formulas, performance targets"},
145
+ {"authority": "Department for Environment", "keywords": "environmental fines, pollution incidents, enforcement costs"},
146
+ {"authority": "London Fire Brigade", "keywords": "response times, equipment costs, staffing levels"},
147
+ {"authority": "West Midlands Fire Service", "keywords": "false alarms, call reduction, prevention costs"},
148
+ {"authority": "Greater Manchester Fire Service", "keywords": "building inspections, safety compliance, enforcement action"},
149
+ {"authority": "South Yorkshire Fire Service", "keywords": "emergency calls, resource deployment, performance metrics"},
150
+ {"authority": "Merseyside Fire Service", "keywords": "community safety, education programs, effectiveness measures"},
151
+ {"authority": "Essex Fire Service", "keywords": "rescue operations, specialist equipment, training costs"},
152
+ {"authority": "Kent Fire Service", "keywords": "station closures, service changes, public consultation"},
153
+ {"authority": "Surrey Fire Service", "keywords": "road accidents, rescue costs, casualty statistics"},
154
+ {"authority": "Hampshire Fire Service", "keywords": "wildfire incidents, prevention measures, resource allocation"},
155
+ {"authority": "Devon Fire Service", "keywords": "coastal rescues, equipment maintenance, operational costs"},
156
+ {"authority": "London Ambulance Service", "keywords": "response targets, performance data, resource pressures"},
157
+ {"authority": "West Midlands Ambulance", "keywords": "call volumes, crew availability, overtime costs"},
158
+ {"authority": "Yorkshire Ambulance Service", "keywords": "hospital handovers, delay times, capacity issues"},
159
+ {"authority": "North West Ambulance Service", "keywords": "emergency calls, triage procedures, response priorities"},
160
+ {"authority": "South Central Ambulance", "keywords": "patient transport, contract costs, service quality"},
161
+ {"authority": "East Midlands Ambulance", "keywords": "staff training, competency assessments, certification costs"},
162
+ {"authority": "South East Coast Ambulance", "keywords": "vehicle maintenance, fleet costs, replacement schedules"},
163
+ {"authority": "East of England Ambulance", "keywords": "clinical governance, audit results, quality improvements"},
164
+ {"authority": "South Western Ambulance", "keywords": "rural coverage, response challenges, resource deployment"},
165
+ {"authority": "Welsh Ambulance Service", "keywords": "cross-border operations, coordination costs, service agreements"},
166
+ {"authority": "Bristol City Council", "keywords": "cycling infrastructure, usage data, maintenance costs"},
167
+ {"authority": "Brighton and Hove Council", "keywords": "seafront management, event licensing, revenue generation"},
168
+ {"authority": "Cambridge City Council", "keywords": "student housing, planning enforcement, compliance monitoring"},
169
+ {"authority": "Canterbury City Council", "keywords": "heritage sites, conservation costs, visitor management"},
170
+ {"authority": "Chester West Council", "keywords": "tourism promotion, marketing spend, economic impact"},
171
+ {"authority": "Colchester Borough Council", "keywords": "business rates, collection performance, appeals process"},
172
+ {"authority": "Durham County Council", "keywords": "mining subsidence, compensation claims, repair costs"},
173
+ {"authority": "Exeter City Council", "keywords": "flood management, defense systems, emergency planning"},
174
+ {"authority": "Gloucester City Council", "keywords": "regeneration projects, funding sources, completion rates"},
175
+ {"authority": "Lancaster City Council", "keywords": "coastal erosion, protection measures, maintenance spending"},
176
+ ]
177
+
178
+ # Create lists for the spinning animation from the combinations above
179
+ ALL_AUTHORITIES_FOR_SPIN = list(set([item["authority"] for item in FOI_COMBINATIONS]))
180
+ ALL_KEYWORDS_FOR_SPIN = list(set(kw.strip() for item in FOI_COMBINATIONS for kw in item["keywords"].split(',')))
181
+
182
+
183
+ # --- Backend Function for Local Inference ---
184
+ @spaces.GPU
185
+ def generate_request_local(authority, kw1, kw2, kw3):
186
+ """Generates a request using the locally loaded transformer model on a dynamically allocated GPU."""
187
+ if not model or not tokenizer:
188
+ return "Error: Model is not loaded. Please check the Space logs for details."
189
+
190
+ keywords = [kw for kw in [kw1, kw2, kw3] if kw]
191
+ keyword_string = ", ".join(keywords)
192
+
193
+ prompt = (
194
+ "You are an expert at writing formal Freedom of Information requests to UK public authorities. "
195
+ f"""Generate a formal Freedom of Information request to {authority} using these keywords: {keyword_string}
196
+ The request should:
197
+ 1. Start with "Dear {authority},"
198
+ 2. Continue with "please provide me with a copy of the following information:"
199
+ 3. Include a numbered list of 1-2 requests for specific information related to the keywords
200
+ 4. The requests should not be too broad, and should be ones that a typical journalist or member of the public might make.
201
+ 5. The requests must not ask for publicly available information
202
+ 6. They must clearly describe the exact information being requested, in a way that doesn't require any subjective judgement
203
+ on the part of the FOI officer to answer the request. No clarification should be needed.
204
+ 7. Do not ask for all policies, or all information
205
+ 8. End with "Yours Faithfully, [Your Name]" exactly
206
+
207
+ Make the requests specific, professional, and relevant to what this public authority would reasonably hold.
208
+ Use accessible language, avoiding terms that are overly legalistic or technical and UK English. Be clear and concise"""
209
+ )
210
+
211
+ try:
212
+ # Tokenize the input prompt
213
+ inputs = tokenizer(prompt, return_tensors="pt")
214
+
215
+ # Move to the model's device (should be cuda when GPU is allocated)
216
+ inputs = inputs.to(model.device)
217
+
218
+ # Set generation parameters to match notebook for better performance
219
+ generation_params = {
220
+ "max_new_tokens": 500,
221
+ "temperature": 0.3,
222
+ "top_p": 0.95,
223
+ "top_k": 50,
224
+ "repetition_penalty": 1.05,
225
+ "do_sample": True,
226
+ "pad_token_id": tokenizer.eos_token_id
227
+ }
228
+
229
+ # Generate text sequences
230
+ output_sequences = model.generate(**inputs, **generation_params)
231
+
232
+ # Decode the generated text, skipping special tokens and the original prompt
233
+ generated_text = tokenizer.decode(
234
+ output_sequences[0][len(inputs["input_ids"][0]):],
235
+ skip_special_tokens=True
236
+ ).strip()
237
+
238
+ # Add this check to remove artifact
239
+ if generated_text.startswith('.\n'):
240
+ generated_text = generated_text[2:]
241
+ return generated_text
242
+
243
+ except Exception as e:
244
+ print(f"Error during generation: {e}")
245
+ print(f"Model device: {model.device if model else 'No model'}")
246
+ print(f"Inputs type: {type(inputs) if 'inputs' in locals() else 'Not created'}")
247
+ return f"An error occurred during text generation: {e}"
248
+
249
+ # --- Gradio UI and Spinning Logic ---
250
+ def spin_the_reels():
251
+ """A generator function that simulates spinning reels and then calls the model."""
252
+ # 1. Simulate the spinning effect
253
+ spin_duration = 2.0 # seconds
254
+ spin_interval = 0.05 # update interval
255
+ start_time = time.time()
256
+ while time.time() - start_time < spin_duration:
257
+ # Yield random values for each reel to create the spinning illusion
258
+ yield (
259
+ random.choice(ALL_AUTHORITIES_FOR_SPIN),
260
+ random.choice(ALL_KEYWORDS_FOR_SPIN),
261
+ random.choice(ALL_KEYWORDS_FOR_SPIN),
262
+ random.choice(ALL_KEYWORDS_FOR_SPIN),
263
+ "Spinning..."
264
+ )
265
+ time.sleep(spin_interval)
266
+
267
+ # 2. Select the final fixed combination
268
+ final_combination = random.choice(FOI_COMBINATIONS)
269
+ final_authority = final_combination["authority"]
270
+
271
+ # Split, strip, and pad keywords to ensure we always have 3 for the UI
272
+ keywords_list = [k.strip() for k in final_combination["keywords"].split(',')]
273
+ keywords_list += [''] * (3 - len(keywords_list)) # Pad with empty strings if < 3
274
+ kw1, kw2, kw3 = keywords_list[:3] # Take the first 3
275
+
276
+ # Display the final reel values and a "Generating..." message
277
+ yield (
278
+ final_authority, kw1, kw2, kw3,
279
+ f"Generating request for {final_authority}...\nPlease wait, this may take a moment."
280
+ )
281
+
282
+ # 3. Call the local model and yield the final result
283
+ generated_request = generate_request_local(final_authority, kw1, kw2, kw3)
284
+ yield (
285
+ final_authority, kw1, kw2, kw3,
286
+ generated_request
287
+ )
288
+
289
+ # --- CSS for Styling ---
290
+ # Added min-width to reduce UI flickering on text change
291
+ reels_css = """
292
+ #reels-container {
293
+ display: flex;
294
+ gap: 1rem;
295
+ margin-bottom: 1rem;
296
+ }
297
+ #reels-container .gradio-textbox {
298
+ text-align: center;
299
+ border: 4px solid #f59e0b;
300
+ border-radius: 12px;
301
+ background-color: #fef3c7;
302
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
303
+ min-width: 150px; /* Prevents resizing/flickering during spin */
304
+ }
305
+ #reels-container .gradio-textbox input {
306
+ font-size: 1.25rem !important;
307
+ font-weight: bold;
308
+ color: #b45309;
309
+ text-align: center;
310
+ }
311
+ #pull-button {
312
+ font-size: 1.5rem !important;
313
+ font-weight: bold;
314
+ padding: 1rem !important;
315
+ box-shadow: 0 8px 15px rgba(0,0,0,0.2);
316
+ transition: all 0.2s ease-in-out;
317
+ }
318
+ #pull-button:hover {
319
+ transform: translateY(-2px);
320
+ box-shadow: 0 10px 20px rgba(0,0,0,0.3);
321
+ }
322
+ """
323
+
324
+ # --- Build the Gradio App ---
325
+ with gr.Blocks(css=reels_css, theme=gr.themes.Soft()) as demo:
326
+ gr.Markdown(
327
+ """
328
+ # FOI Request-O-Matic 🎰
329
+ Press the button to generate a Freedom of Information request.
330
+ """
331
+ )
332
+ with gr.Row(elem_id="reels-container"):
333
+ reel1 = gr.Textbox(label="Authority", interactive=False, elem_id="reel-1", scale=2)
334
+ reel2 = gr.Textbox(label="Keyword 1", interactive=False, elem_id="reel-2", scale=1)
335
+ reel3 = gr.Textbox(label="Keyword 2", interactive=False, elem_id="reel-3", scale=1)
336
+ reel4 = gr.Textbox(label="Keyword 3", interactive=False, elem_id="reel-4", scale=1)
337
+
338
+ pull_button = gr.Button("Generate a request", variant="primary", elem_id="pull-button")
339
+
340
+ output_request = gr.Textbox(
341
+ label="Generated FOI Request",
342
+ lines=15,
343
+ interactive=True,
344
+ show_copy_button=True
345
+ )
346
+
347
+ pull_button.click(
348
+ fn=spin_the_reels,
349
+ inputs=[],
350
+ outputs=[reel1, reel2, reel3, reel4, output_request]
351
+ )
352
+
353
+ if __name__ == "__main__":
354
+ demo.launch()