HMC83 commited on
Commit
8badf3b
·
verified ·
1 Parent(s): cd158a8

Update app.py

Browse files

reverting to old logic

Files changed (1) hide show
  1. app.py +248 -53
app.py CHANGED
@@ -3,6 +3,7 @@ import os
3
  import random
4
  import time
5
  import torch
 
6
  from transformers import AutoTokenizer, AutoModelForCausalLM
7
  import spaces
8
 
@@ -23,18 +24,179 @@ except Exception as e:
23
  model = None
24
  tokenizer = None
25
 
26
- # --- Data for the Reels (Truncated for brevity) ---
 
27
  FOI_COMBINATIONS = [
28
  {"authority": "Borders NHS Board", "keywords": "whistleblowing guidance, wrongdoing, public body"},
29
  {"authority": "Borders NHS Board", "keywords": "ethical support, clinical triage, minutes"},
30
  {"authority": "Buckinghamshire Fire and Rescue Service", "keywords": "fire service, fire officers, membership"},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  {"authority": "Lancaster City Council", "keywords": "coastal erosion, protection measures, maintenance spending"},
32
  ]
33
 
34
-
35
  ALL_AUTHORITIES_FOR_SPIN = list(set([item["authority"] for item in FOI_COMBINATIONS]))
36
  ALL_KEYWORDS_FOR_SPIN = list(set(kw.strip() for item in FOI_COMBINATIONS for kw in item["keywords"].split(',')))
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  # --- Helper: wrap content in the FOI letter template ---
39
  def wrap_in_letter(authority: str, body: str) -> str:
40
  body = body.strip()
@@ -46,15 +208,81 @@ def wrap_in_letter(authority: str, body: str) -> str:
46
  )
47
  return template
48
 
49
- # --- Gradio UI and Spinning Logic ---
50
- # --- Main Backend Function for Gradio ---
51
  @spaces.GPU
52
- def spin_and_generate():
53
- # 1. Spin the reels for visual effect
54
- spin_duration = 2.0
55
- spin_interval = 0.05
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  start_time = time.time()
57
  while time.time() - start_time < spin_duration:
 
58
  yield (
59
  random.choice(ALL_AUTHORITIES_FOR_SPIN),
60
  random.choice(ALL_KEYWORDS_FOR_SPIN),
@@ -64,63 +292,30 @@ def spin_and_generate():
64
  )
65
  time.sleep(spin_interval)
66
 
67
- # 2. Select the final combination and update UI
68
  final_combination = random.choice(FOI_COMBINATIONS)
69
  final_authority = final_combination["authority"]
 
 
70
  keywords_list = [k.strip() for k in final_combination["keywords"].split(',')]
71
- keywords_list += [''] * (3 - len(keywords_list))
72
- kw1, kw2, kw3 = keywords_list[:3]
73
 
 
74
  yield (
75
  final_authority, kw1, kw2, kw3,
76
  f"Generating request for {final_authority}...\nPlease wait, this may take a moment."
77
  )
78
 
79
- # 3. Generate the request using the model
80
- if not model or not tokenizer:
81
- yield (final_authority, kw1, kw2, kw3, "Error: Model is not loaded. Check Space logs.")
82
- return
83
-
84
- keywords = [kw for kw in [kw1, kw2, kw3] if kw]
85
- keyword_string = ", ".join(keywords)
86
- prompt = (
87
- "You are an expert at writing formal Freedom of Information requests to UK public authorities. "
88
- f"Generate the request text (without greeting or signature) for {final_authority}, using these keywords: {keyword_string}."
89
- )
90
-
91
- try:
92
- inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
93
- generation_params = {
94
- "max_new_tokens": 250,
95
- "do_sample": True,
96
- "temperature": 0.25,
97
- "top_k": 50,
98
- "top_p": 0.95,
99
- "repetition_penalty": 1.1,
100
- "eos_token_id": tokenizer.eos_token_id
101
- }
102
- output_sequences = model.generate(**inputs, **generation_params)
103
- generated_text = tokenizer.decode(
104
- output_sequences[0][len(inputs["input_ids"][0]):],
105
- skip_special_tokens=True
106
- ).strip()
107
-
108
- if generated_text.startswith('.\n'):
109
- generated_text = generated_text[2:]
110
-
111
- final_request = wrap_in_letter(final_authority, generated_text)
112
-
113
- except Exception as e:
114
- final_request = f"An error occurred during text generation: {e}"
115
-
116
- # 4. Yield the final result to the UI
117
  yield (
118
  final_authority, kw1, kw2, kw3,
119
- final_request
120
  )
121
 
122
-
123
  # --- CSS for Styling ---
 
124
  reels_css = """
125
  #reels-container {
126
  display: flex;
@@ -133,7 +328,7 @@ reels_css = """
133
  border-radius: 12px;
134
  background-color: #fef3c7;
135
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
136
- min-width: 150px;
137
  }
138
  #reels-container .gradio-textbox input {
139
  font-size: 1.25rem !important;
@@ -178,7 +373,7 @@ with gr.Blocks(css=reels_css, theme=gr.themes.Soft()) as demo:
178
  )
179
 
180
  pull_button.click(
181
- fn=spin_and_generate, # Changed to the new combined function
182
  inputs=[],
183
  outputs=[reel1, reel2, reel3, reel4, output_request]
184
  )
 
3
  import random
4
  import time
5
  import torch
6
+ import re # <-- NEW
7
  from transformers import AutoTokenizer, AutoModelForCausalLM
8
  import spaces
9
 
 
24
  model = None
25
  tokenizer = None
26
 
27
+ # --- Data for the Reels ---
28
+ # A list of authority and keyword combinations.
29
  FOI_COMBINATIONS = [
30
  {"authority": "Borders NHS Board", "keywords": "whistleblowing guidance, wrongdoing, public body"},
31
  {"authority": "Borders NHS Board", "keywords": "ethical support, clinical triage, minutes"},
32
  {"authority": "Buckinghamshire Fire and Rescue Service", "keywords": "fire service, fire officers, membership"},
33
+ {"authority": "Ballymoney Borough Council", "keywords": "parking enforcement, employee responsibility, parking services"},
34
+ {"authority": "British Council", "keywords": "British Council funding, festival, correspondence"},
35
+ {"authority": "Cardiff and Vale University Health Board", "keywords": "drugs, therapeutic, meetings"},
36
+ {"authority": "Hastings Borough Council", "keywords": "local authority care, protection orders, adopted children"},
37
+ {"authority": "Sevenoaks District Council", "keywords": "residential property, council tax, postcode"},
38
+ {"authority": "The Marine Society and Sea Cadets", "keywords": "training, sea cadets, years in office"},
39
+ {"authority": "West Yorkshire Police Authority", "keywords": "professional standards, official duties, chief constable"},
40
+ {"authority": "Royal College of Veterinary Surgeons", "keywords": "veterinary practices, veterinary hospitals, contact details"},
41
+ {"authority": "South Tyneside Metropolitan Borough Council", "keywords": "commercial customers, waste collection, waste producers"},
42
+ {"authority": "University of Surrey", "keywords": "university car parks, parking tickets, parking enforcement"},
43
+ {"authority": "King's College Hospital NHS Trust", "keywords": "annual spend, medical equipment, procurement"},
44
+ {"authority": "Great Ormond Street Hospital", "keywords": "charity donations, research funding, financial year"},
45
+ {"authority": "Public Health England", "keywords": "vaccination costs, public health campaigns, effectiveness"},
46
+ {"authority": "NHS Digital", "keywords": "IT spending, system outages, cyber security"},
47
+ {"authority": "Cornwall Council", "keywords": "adult social care, spending, assessments"},
48
+ {"authority": "Hertfordshire County Council", "keywords": "school transport, route costs, contractor payments"},
49
+ {"authority": "Wiltshire Council", "keywords": "highway maintenance, pothole repairs, annual spend"},
50
+ {"authority": "Surrey County Council", "keywords": "council tax, collection rates, arrears"},
51
+ {"authority": "Ofgem", "keywords": "energy company, penalties, investigations"},
52
+ {"authority": "Financial Conduct Authority", "keywords": "enforcement action, fines imposed, complaint statistics"},
53
+ {"authority": "Competition and Markets Authority", "keywords": "merger investigations, market studies, legal costs"},
54
+ {"authority": "Health and Safety Executive", "keywords": "workplace accidents, enforcement notices, prosecution costs"},
55
+ {"authority": "Network Rail", "keywords": "delay compensation, signal failures, maintenance costs"},
56
+ {"authority": "Highways England", "keywords": "road maintenance, contractor spending, traffic flow"},
57
+ {"authority": "Civil Aviation Authority", "keywords": "airline complaints, enforcement action, safety investigations"},
58
+ {"authority": "HM Revenue and Customs", "keywords": "tax investigations, recovery rates, compliance costs"},
59
+ {"authority": "Department for Work and Pensions", "keywords": "benefit sanctions, appeal outcomes, administrative costs"},
60
+ {"authority": "Nottinghamshire County Council", "keywords": "adult social care, budget allocation, waiting lists"},
61
+ {"authority": "Staffordshire County Council", "keywords": "school meals, contract costs, nutritional standards"},
62
+ {"authority": "Derbyshire County Council", "keywords": "library services, closure consultations, usage statistics"},
63
+ {"authority": "East Sussex County Council", "keywords": "children services, safeguarding, staffing costs"},
64
+ {"authority": "Northumberland County Council", "keywords": "waste collection, recycling rates, contractor performance"},
65
+ {"authority": "Royal Devon NHS Trust", "keywords": "patient complaints, response times, resolution outcomes"},
66
+ {"authority": "Sheffield Teaching Hospitals", "keywords": "medical equipment, procurement costs, maintenance contracts"},
67
+ {"authority": "Bradford Teaching Hospitals", "keywords": "staff training, external courses, annual expenditure"},
68
+ {"authority": "Northampton General Hospital", "keywords": "parking charges, revenue generated, patient feedback"},
69
+ {"authority": "Environment Agency", "keywords": "flood defenses, maintenance costs, effectiveness assessments"},
70
+ {"authority": "Natural England", "keywords": "habitat protection, designation costs, landowner compensation"},
71
+ {"authority": "Marine Management Organisation", "keywords": "fishing licenses, enforcement costs, violation penalties"},
72
+ {"authority": "Forestry Commission", "keywords": "timber sales, revenue targets, environmental impact"},
73
+ {"authority": "Welsh Government", "keywords": "language services, translation costs, usage statistics"},
74
+ {"authority": "Scottish Government", "keywords": "ferry subsidies, route profitability, passenger numbers"},
75
+ {"authority": "Northern Ireland Executive", "keywords": "cross-border initiatives, funding allocation, outcomes"},
76
+ {"authority": "Office for National Statistics", "keywords": "census costs, data collection, contractor payments"},
77
+ {"authority": "Land Registry", "keywords": "property registrations, processing times, fee income"},
78
+ {"authority": "Companies House", "keywords": "late filing penalties, collection rates, enforcement action"},
79
+ {"authority": "Intellectual Property Office", "keywords": "patent applications, processing costs, appeal outcomes"},
80
+ {"authority": "InGen Safety Authority", "keywords": "containment breaches, insurance costs, visitor incidents"},
81
+ {"authority": "Natural England", "keywords": "Bigfoot sightings, habitat, evidence"},
82
+ {"authority": "The Royal Parks", "keywords": "squirrel uprising, acorn stockpiles, intelligence"},
83
+ {"authority": "Midsomer Constabulary", "keywords": "unusually high murder rate, statistics, investigations"},
84
+ {"authority": "Scottish Government", "keywords": "Loch Ness Monster, research, funding"},
85
+ {"authority": "Westminster City Council", "keywords": "noise complaints, nightlife, enforcement"},
86
+ {"authority": "Network Rail", "keywords": "delays, compensation, signal failures"},
87
+ {"authority": "Gloucestershire County Council", "keywords": "adult social care, assessment costs, service provision"},
88
+ {"authority": "Worcestershire County Council", "keywords": "school transport, route planning, contractor performance"},
89
+ {"authority": "Leicestershire County Council", "keywords": "highway maintenance, annual spend, emergency repairs"},
90
+ {"authority": "Warwickshire County Council", "keywords": "library services, usage statistics, closure consultations"},
91
+ {"authority": "Nottinghamshire County Council", "keywords": "children services, safeguarding referrals, staff costs"},
92
+ {"authority": "Derbyshire County Council", "keywords": "waste disposal, recycling rates, collection contracts"},
93
+ {"authority": "Staffordshire County Council", "keywords": "care homes, inspection reports, quality ratings"},
94
+ {"authority": "Shropshire Council", "keywords": "planning applications, approval rates, enforcement action"},
95
+ {"authority": "Herefordshire Council", "keywords": "council tax, collection rates, arrears management"},
96
+ {"authority": "Cheshire East Council", "keywords": "parking enforcement, penalty notices, appeal outcomes"},
97
+ {"authority": "Royal Liverpool University Hospital", "keywords": "patient complaints, response times, resolution outcomes"},
98
+ {"authority": "Sheffield Teaching Hospitals NHS Trust", "keywords": "medical equipment, procurement costs, maintenance contracts"},
99
+ {"authority": "Leeds Teaching Hospitals NHS Trust", "keywords": "agency staff, temporary costs, recruitment spend"},
100
+ {"authority": "Newcastle upon Tyne Hospitals NHS Trust", "keywords": "cancelled operations, reasons given, patient impact"},
101
+ {"authority": "University Hospitals Birmingham", "keywords": "waiting times, treatment delays, performance targets"},
102
+ {"authority": "Guy's and St Thomas' NHS Trust", "keywords": "infection control, outbreak incidents, prevention costs"},
103
+ {"authority": "King's College Hospital NHS Trust", "keywords": "emergency admissions, capacity management, staffing levels"},
104
+ {"authority": "Royal Free NHS Trust", "keywords": "clinical trials, research funding, patient consent"},
105
+ {"authority": "Imperial College Healthcare NHS Trust", "keywords": "private patient, income generated, capacity allocation"},
106
+ {"authority": "Barts Health NHS Trust", "keywords": "financial deficit, recovery plans, cost reduction"},
107
+ {"authority": "West Yorkshire Police", "keywords": "response times, emergency calls, performance standards"},
108
+ {"authority": "South Yorkshire Police", "keywords": "crime statistics, detection rates, resource allocation"},
109
+ {"authority": "North Yorkshire Police", "keywords": "rural policing, coverage areas, response costs"},
110
+ {"authority": "Humberside Police", "keywords": "traffic enforcement, speed cameras, revenue generated"},
111
+ {"authority": "Lincolnshire Police", "keywords": "domestic violence, incident reports, support services"},
112
+ {"authority": "Nottinghamshire Police", "keywords": "stop and search, statistics recorded, outcome data"},
113
+ {"authority": "Derbyshire Police", "keywords": "drug seizures, investigation costs, conviction rates"},
114
+ {"authority": "Leicestershire Police", "keywords": "neighbourhood policing, community engagement, effectiveness measures"},
115
+ {"authority": "Warwickshire Police", "keywords": "police stations, closure plans, public consultations"},
116
+ {"authority": "West Mercia Police", "keywords": "cybercrime, investigation resources, training costs"},
117
+ {"authority": "Environment Agency", "keywords": "flood defenses, maintenance spending, effectiveness assessments"},
118
+ {"authority": "Natural England", "keywords": "habitat designations, compensation payments, landowner agreements"},
119
+ {"authority": "Historic England", "keywords": "listed buildings, consent applications, enforcement cases"},
120
+ {"authority": "Highways England", "keywords": "motorway maintenance, contractor payments, performance indicators"},
121
+ {"authority": "Network Rail", "keywords": "signal failures, delay minutes, compensation costs"},
122
+ {"authority": "Civil Aviation Authority", "keywords": "airline complaints, resolution times, enforcement action"},
123
+ {"authority": "Maritime and Coastguard Agency", "keywords": "rescue operations, helicopter costs, response times"},
124
+ {"authority": "Driver and Vehicle Standards Agency", "keywords": "MOT failures, test center, compliance rates"},
125
+ {"authority": "Vehicle Certification Agency", "keywords": "emissions testing, approval procedures, manufacturer compliance"},
126
+ {"authority": "Planning Inspectorate", "keywords": "appeal decisions, processing times, outcome statistics"},
127
+ {"authority": "Health and Safety Executive", "keywords": "workplace accidents, investigation costs, prosecution outcomes"},
128
+ {"authority": "Care Quality Commission", "keywords": "inspection reports, rating changes, enforcement action"},
129
+ {"authority": "Ofgem", "keywords": "energy company, penalties imposed, consumer complaints"},
130
+ {"authority": "Ofwat", "keywords": "water companies, price reviews, performance monitoring"},
131
+ {"authority": "Ofcom", "keywords": "broadcasting complaints, investigation procedures, penalty decisions"},
132
+ {"authority": "Financial Conduct Authority", "keywords": "financial services, enforcement cases, penalty amounts"},
133
+ {"authority": "Competition and Markets Authority", "keywords": "merger investigations, market studies, intervention costs"},
134
+ {"authority": "Gambling Commission", "keywords": "license suspensions, operator penalties, compliance monitoring"},
135
+ {"authority": "Information Commissioner's Office", "keywords": "data breaches, penalty notices, investigation outcomes"},
136
+ {"authority": "Electoral Commission", "keywords": "campaign spending, compliance checks, penalty procedures"},
137
+ {"authority": "HM Revenue and Customs", "keywords": "tax investigations, recovery amounts, compliance costs"},
138
+ {"authority": "HM Treasury", "keywords": "departmental budgets, spending reviews, efficiency targets"},
139
+ {"authority": "Cabinet Office", "keywords": "government consultancy, external advisors, procurement costs"},
140
+ {"authority": "Ministry of Justice", "keywords": "court delays, case backlogs, administrative costs"},
141
+ {"authority": "Home Office", "keywords": "immigration appeals, processing times, detention costs"},
142
+ {"authority": "Department for Transport", "keywords": "railway subsidies, franchise performance, punctuality targets"},
143
+ {"authority": "Department for Education", "keywords": "academy conversions, funding allocations, performance data"},
144
+ {"authority": "Department for Work and Pensions", "keywords": "benefit sanctions, appeal success, administrative costs"},
145
+ {"authority": "Department of Health", "keywords": "NHS funding, allocation formulas, performance targets"},
146
+ {"authority": "Department for Environment", "keywords": "environmental fines, pollution incidents, enforcement costs"},
147
+ {"authority": "London Fire Brigade", "keywords": "response times, equipment costs, staffing levels"},
148
+ {"authority": "West Midlands Fire Service", "keywords": "false alarms, call reduction, prevention costs"},
149
+ {"authority": "Greater Manchester Fire Service", "keywords": "building inspections, safety compliance, enforcement action"},
150
+ {"authority": "South Yorkshire Fire Service", "keywords": "emergency calls, resource deployment, performance metrics"},
151
+ {"authority": "Merseyside Fire Service", "keywords": "community safety, education programs, effectiveness measures"},
152
+ {"authority": "Essex Fire Service", "keywords": "rescue operations, specialist equipment, training costs"},
153
+ {"authority": "Kent Fire Service", "keywords": "station closures, service changes, public consultation"},
154
+ {"authority": "Surrey Fire Service", "keywords": "road accidents, rescue costs, casualty statistics"},
155
+ {"authority": "Hampshire Fire Service", "keywords": "wildfire incidents, prevention measures, resource allocation"},
156
+ {"authority": "Devon Fire Service", "keywords": "coastal rescues, equipment maintenance, operational costs"},
157
+ {"authority": "London Ambulance Service", "keywords": "response targets, performance data, resource pressures"},
158
+ {"authority": "West Midlands Ambulance", "keywords": "call volumes, crew availability, overtime costs"},
159
+ {"authority": "Yorkshire Ambulance Service", "keywords": "hospital handovers, delay times, capacity issues"},
160
+ {"authority": "North West Ambulance Service", "keywords": "emergency calls, triage procedures, response priorities"},
161
+ {"authority": "South Central Ambulance", "keywords": "patient transport, contract costs, service quality"},
162
+ {"authority": "East Midlands Ambulance", "keywords": "staff training, competency assessments, certification costs"},
163
+ {"authority": "South East Coast Ambulance", "keywords": "vehicle maintenance, fleet costs, replacement schedules"},
164
+ {"authority": "East of England Ambulance", "keywords": "clinical governance, audit results, quality improvements"},
165
+ {"authority": "South Western Ambulance", "keywords": "rural coverage, response challenges, resource deployment"},
166
+ {"authority": "Welsh Ambulance Service", "keywords": "cross-border operations, coordination costs, service agreements"},
167
+ {"authority": "Bristol City Council", "keywords": "cycling infrastructure, usage data, maintenance costs"},
168
+ {"authority": "Brighton and Hove Council", "keywords": "seafront management, event licensing, revenue generation"},
169
+ {"authority": "Cambridge City Council", "keywords": "student housing, planning enforcement, compliance monitoring"},
170
+ {"authority": "Canterbury City Council", "keywords": "heritage sites, conservation costs, visitor management"},
171
+ {"authority": "Chester West Council", "keywords": "tourism promotion, marketing spend, economic impact"},
172
+ {"authority": "Colchester Borough Council", "keywords": "business rates, collection performance, appeals process"},
173
+ {"authority": "Durham County Council", "keywords": "mining subsidence, compensation claims, repair costs"},
174
+ {"authority": "Exeter City Council", "keywords": "flood management, defense systems, emergency planning"},
175
+ {"authority": "Gloucester City Council", "keywords": "regeneration projects, funding sources, completion rates"},
176
  {"authority": "Lancaster City Council", "keywords": "coastal erosion, protection measures, maintenance spending"},
177
  ]
178
 
179
+ # Create lists for the spinning animation from the combinations above
180
  ALL_AUTHORITIES_FOR_SPIN = list(set([item["authority"] for item in FOI_COMBINATIONS]))
181
  ALL_KEYWORDS_FOR_SPIN = list(set(kw.strip() for item in FOI_COMBINATIONS for kw in item["keywords"].split(',')))
182
 
183
+ # --- Helper: clean model output into a numbered list starting at "1." ---
184
+ def clean_and_validate_output(text: str):
185
+ """
186
+ Extract the main numbered list starting at '1.' and strip any closing signature lines.
187
+ Always returns cleaned text and a boolean flag (True = looks fine).
188
+ """
189
+ # Keep everything from the first "1." onward, if present.
190
+ m = re.search(r'(?m)^\s*1\.\s', text)
191
+ body = text[m.start():].strip() if m else text.strip()
192
+
193
+ # Remove common signature lines at the end (best-effort).
194
+ body = re.sub(r'(?im)^\s*(yours.*|kind regards.*|regards.*)$', '', body).strip()
195
+
196
+ # If it doesn't contain at least one numbered point, it's still usable, but we mark as not strictly-valid.
197
+ is_valid = bool(re.search(r'(?m)^\s*\d+\.\s', body))
198
+ return body, is_valid
199
+
200
  # --- Helper: wrap content in the FOI letter template ---
201
  def wrap_in_letter(authority: str, body: str) -> str:
202
  body = body.strip()
 
208
  )
209
  return template
210
 
211
+ # --- Backend Function for Local Inference ---
 
212
  @spaces.GPU
213
+ def generate_request_local(authority, kw1, kw2, kw3):
214
+ """Generates a request using the locally loaded transformer model, with validation and retry logic."""
215
+ if not model or not tokenizer:
216
+ return "Error: Model is not loaded. Please check the Space logs for details."
217
+
218
+ keywords = [kw for kw in [kw1, kw2, kw3] if kw]
219
+ keyword_string = ", ".join(keywords)
220
+ prompt = (
221
+ "You are an expert at writing formal Freedom of Information requests to UK public authorities. "
222
+ f"Generate ONLY the numbered list of the specific information being requested, starting at '1.' "
223
+ f"for {authority}, using these keywords: {keyword_string}. "
224
+ "Do not include greetings or signatures."
225
+ )
226
+
227
+ max_retries = 2
228
+ for attempt in range(max_retries):
229
+ try:
230
+ # Tokenize the input prompt
231
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
232
+
233
+ # Set generation parameters
234
+ generation_params = {
235
+ "max_new_tokens": 250,
236
+ "do_sample": True,
237
+ "temperature": 0.25,
238
+ "top_k": 50,
239
+ "top_p": 0.95,
240
+ "repetition_penalty": 1.1,
241
+ "streamer": None,
242
+ "eos_token_id": tokenizer.eos_token_id
243
+ }
244
+
245
+ # Generate text sequences
246
+ output_sequences = model.generate(**inputs, **generation_params)
247
+
248
+ # Decode the generated text
249
+ generated_text = tokenizer.decode(
250
+ output_sequences[0][len(inputs["input_ids"][0]):],
251
+ skip_special_tokens=True
252
+ ).strip()
253
+
254
+ # Remove artifact if present
255
+ if generated_text.startswith('.\n'):
256
+ generated_text = generated_text[2:]
257
+
258
+ # Clean and validate the output
259
+ cleaned_text, is_valid = clean_and_validate_output(generated_text)
260
+
261
+ # Wrap in the letter template regardless; validation just influences retry behavior
262
+ letter = wrap_in_letter(authority, cleaned_text)
263
+
264
+ if is_valid:
265
+ return letter
266
+ else:
267
+ print(f"Attempt {attempt + 1}/{max_retries}: Output lacked clear numbering. Retrying...")
268
+
269
+ except Exception as e:
270
+ print(f"Error during generation attempt {attempt + 1}/{max_retries}: {e}")
271
+ if attempt == max_retries - 1:
272
+ return f"An error occurred during text generation: {e}"
273
+
274
+ # If retries failed, return the best effort letter using the last cleaned text we had
275
+ return wrap_in_letter(authority, "1. [Unable to format automatically] Please restate the information requested.\n2. [Optional second point]")
276
+
277
+ # --- Gradio UI and Spinning Logic ---
278
+ def spin_the_reels():
279
+ """A generator function that simulates spinning reels and then calls the model."""
280
+ # 1. Simulate the spinning effect
281
+ spin_duration = 2.0 # seconds
282
+ spin_interval = 0.05 # update interval
283
  start_time = time.time()
284
  while time.time() - start_time < spin_duration:
285
+ # Yield random values for each reel to create the spinning illusion
286
  yield (
287
  random.choice(ALL_AUTHORITIES_FOR_SPIN),
288
  random.choice(ALL_KEYWORDS_FOR_SPIN),
 
292
  )
293
  time.sleep(spin_interval)
294
 
295
+ # 2. Select the final fixed combination
296
  final_combination = random.choice(FOI_COMBINATIONS)
297
  final_authority = final_combination["authority"]
298
+
299
+ # Split, strip, and pad keywords to ensure we always have 3 for the UI
300
  keywords_list = [k.strip() for k in final_combination["keywords"].split(',')]
301
+ keywords_list += [''] * (3 - len(keywords_list)) # Pad with empty strings if < 3
302
+ kw1, kw2, kw3 = keywords_list[:3] # Take the first 3
303
 
304
+ # Display the final reel values and a "Generating..." message
305
  yield (
306
  final_authority, kw1, kw2, kw3,
307
  f"Generating request for {final_authority}...\nPlease wait, this may take a moment."
308
  )
309
 
310
+ # 3. Call the local model and yield the final result
311
+ generated_request = generate_request_local(final_authority, kw1, kw2, kw3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  yield (
313
  final_authority, kw1, kw2, kw3,
314
+ generated_request
315
  )
316
 
 
317
  # --- CSS for Styling ---
318
+ # Added min-width to reduce UI flickering on text change
319
  reels_css = """
320
  #reels-container {
321
  display: flex;
 
328
  border-radius: 12px;
329
  background-color: #fef3c7;
330
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
331
+ min-width: 150px; /* Prevents resizing/flickering during spin */
332
  }
333
  #reels-container .gradio-textbox input {
334
  font-size: 1.25rem !important;
 
373
  )
374
 
375
  pull_button.click(
376
+ fn=spin_the_reels,
377
  inputs=[],
378
  outputs=[reel1, reel2, reel3, reel4, output_request]
379
  )