davda54 commited on
Commit
086869b
·
verified ·
1 Parent(s): 652a95d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -51
app.py CHANGED
@@ -7,8 +7,10 @@ import random
7
  from datetime import datetime
8
  from typing import Dict, List, Tuple
9
  import hashlib
10
- from datasets import load_dataset
11
  import itertools
 
 
 
12
 
13
  from collections.abc import Iterable
14
 
@@ -215,6 +217,10 @@ TODO
215
  **Code or mathematical expressions**: If responses contain code snippets or mathematical expressions, evaluate only the fluency of the natural language portions.
216
  """
217
 
 
 
 
 
218
  HF_TOKEN = os.environ.get("HF_TOKEN")
219
 
220
  # Model names for the three responses
@@ -289,55 +295,109 @@ class AnnotationManager:
289
  def __init__(self):
290
  self.annotations = {} # Store annotations by user_id
291
  self.user_states = {} # Track each user's progress
 
 
 
 
 
292
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  def get_user_seed(self, user_id: str) -> int:
294
  """Generate consistent seed for user"""
295
- return int(hashlib.md5(user_id.encode()).hexdigest(), 16) % 100000
296
-
297
  def get_user_samples(self, user_id: str) -> List[Dict]:
298
  """Get shuffled samples for user based on their ID"""
299
  seed = self.get_user_seed(user_id)
300
- samples = DATASET_SAMPLES.copy() # Use loaded dataset
301
  random.Random(seed).shuffle(samples)
302
- samples = [
303
- sample if random.Random(seed + i).randint(0, 1) == 0 else swap_sample(sample)
304
- for i, sample in enumerate(samples)
305
- ]
306
  return samples
307
 
308
- def save_annotation(self, user_id: str, sample_id: str, choice: str,
309
- model_a: str = None, model_b: str = None):
310
- """Save user's annotation with model information"""
311
- if user_id not in self.annotations:
312
- self.annotations[user_id] = []
313
-
314
- annotation = {
315
- "user_id": user_id,
316
- "sample_id": sample_id,
317
- "choice": choice,
318
- "model_a": model_a,
319
- "model_b": model_b,
320
- "timestamp": datetime.now().isoformat()
321
- }
322
-
323
- self.annotations[user_id].append(annotation)
324
-
325
- # Update user state
326
- if user_id in self.user_states:
327
- self.user_states[user_id]["annotations"].append(sample_id)
328
- self.user_states[user_id]["current_index"] += 1
329
-
330
- # In production, save to HuggingFace dataset here
331
- print(f"Saved annotation: {annotation}")
332
-
333
- def get_user_progress(self, user_id: str) -> Dict:
334
- """Get user's annotation progress"""
335
- if user_id not in self.annotations:
336
- return {"completed": 0, "total": len(DATASET_SAMPLES)}
337
-
338
- completed = len(self.annotations[user_id])
339
- return {"completed": completed, "total": len(DATASET_SAMPLES)}
340
-
341
  def get_next_sample(self, user_id: str) -> Tuple[Dict, int, int]:
342
  """Get next unannotated sample for user"""
343
  if user_id not in self.user_states:
@@ -349,12 +409,13 @@ class AnnotationManager:
349
  samples = self.get_user_samples(user_id)
350
  state = self.user_states[user_id]
351
 
 
 
 
352
  # Find next unannotated sample
353
- while state["current_index"] < len(samples):
354
- sample = samples[state["current_index"]]
355
  if not self.is_annotated(user_id, sample["id"]):
356
- return sample, state["current_index"] + 1, len(samples)
357
- state["current_index"] += 1
358
 
359
  # All samples annotated
360
  return None, len(samples), len(samples)
@@ -364,6 +425,94 @@ class AnnotationManager:
364
  if user_id not in self.annotations:
365
  return False
366
  return any(ann["sample_id"] == sample_id for ann in self.annotations[user_id])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
 
368
 
369
  # Initialize manager
@@ -406,7 +555,8 @@ def login(user_id: str) -> Tuple:
406
  gr.update(value=sample["prompt"]), # prompt
407
  gr.update(value=sample["response_a"]), # response_a
408
  gr.update(value=sample["response_b"]), # response_b
409
- gr.update(value=f"Progress: {current}/{total}") # progress
 
410
  )
411
 
412
  def annotate(choice: str, user_id: str) -> Tuple:
@@ -429,13 +579,13 @@ def annotate(choice: str, user_id: str) -> Tuple:
429
  "b_better": "B is more fluent",
430
  "equal": "Equally fluent"
431
  }
432
- # Save with model information
 
433
  manager.save_annotation(
434
  user_id,
435
  sample["id"],
436
  choice_map[choice],
437
- model_a=sample.get("model_a"),
438
- model_b=sample.get("model_b")
439
  )
440
 
441
  # Get next sample
@@ -450,12 +600,15 @@ def annotate(choice: str, user_id: str) -> Tuple:
450
  gr.update(value="All annotations complete!", visible=True) # status
451
  )
452
 
 
 
 
453
  return (
454
  gr.update(value=next_sample["prompt"]), # prompt
455
  gr.update(value=next_sample["response_a"]), # response_a
456
  gr.update(value=next_sample["response_b"]), # response_b
457
- gr.update(value=f"Progress: {current}/{total} | Comparing: {sample.get('model_a', 'A')} vs {sample.get('model_b', 'B')}"), # progress
458
- # gr.update(value=f"Progress: {current}/{total}"), # progress
459
  gr.update(value="Annotation saved!", visible=True) # status
460
  )
461
 
 
7
  from datetime import datetime
8
  from typing import Dict, List, Tuple
9
  import hashlib
 
10
  import itertools
11
+ from datasets import load_dataset, Dataset, DatasetDict
12
+ from huggingface_hub import HfApi, create_repo, repo_exists
13
+ import threading
14
 
15
  from collections.abc import Iterable
16
 
 
217
  **Code or mathematical expressions**: If responses contain code snippets or mathematical expressions, evaluate only the fluency of the natural language portions.
218
  """
219
 
220
+ # Configuration for the output dataset
221
+ OUTPUT_DATASET_NAME = "ltg/fluency-annotations" # Change to your desired dataset name
222
+ OUTPUT_DATASET_PRIVATE = True # Keep the annotations dataset private
223
+
224
  HF_TOKEN = os.environ.get("HF_TOKEN")
225
 
226
  # Model names for the three responses
 
295
  def __init__(self):
296
  self.annotations = {} # Store annotations by user_id
297
  self.user_states = {} # Track each user's progress
298
+ self.annotation_cache = [] # Cache for batch uploads
299
+ self.lock = threading.Lock() # Thread safety for annotations
300
+
301
+ # Initialize or load existing annotations dataset
302
+ self.init_annotations_dataset()
303
 
304
+ def init_annotations_dataset(self):
305
+ """Initialize or load existing annotations from HuggingFace"""
306
+ try:
307
+ if HF_TOKEN:
308
+ api = HfApi(token=HF_TOKEN)
309
+
310
+ # Check if dataset exists, if not create it
311
+ if not repo_exists(OUTPUT_DATASET_NAME, repo_type="dataset", token=HF_TOKEN):
312
+ print(f"Creating new dataset: {OUTPUT_DATASET_NAME}")
313
+ create_repo(
314
+ OUTPUT_DATASET_NAME,
315
+ repo_type="dataset",
316
+ private=OUTPUT_DATASET_PRIVATE,
317
+ token=HF_TOKEN
318
+ )
319
+ # Create empty dataset structure
320
+ self.push_empty_dataset()
321
+ else:
322
+ # Load existing annotations
323
+ print(f"Loading existing annotations from {OUTPUT_DATASET_NAME}")
324
+ self.load_existing_annotations()
325
+ else:
326
+ print("Warning: No HF_TOKEN found. Annotations will only be saved locally.")
327
+ except Exception as e:
328
+ print(f"Error initializing annotations dataset: {e}")
329
+ print("Continuing with local-only mode")
330
+
331
+ def push_empty_dataset(self):
332
+ """Create and push empty dataset structure"""
333
+ try:
334
+ empty_data = {
335
+ "user_id": [],
336
+ "sample_id": [],
337
+ "original_id": [],
338
+ "model_a": [],
339
+ "model_b": [],
340
+ "choice": [],
341
+ "prompt": [],
342
+ "response_a": [],
343
+ "response_b": [],
344
+ "dataset": [],
345
+ "timestamp": []
346
+ }
347
+
348
+ dataset = Dataset.from_dict(empty_data)
349
+ dataset.push_to_hub(OUTPUT_DATASET_NAME, token=HF_TOKEN, private=OUTPUT_DATASET_PRIVATE)
350
+ print(f"Created empty dataset at {OUTPUT_DATASET_NAME}")
351
+ except Exception as e:
352
+ print(f"Error creating empty dataset: {e}")
353
+
354
+ def load_existing_annotations(self):
355
+ """Load existing annotations from HuggingFace dataset"""
356
+ try:
357
+ dataset = load_dataset(OUTPUT_DATASET_NAME, split="train", token=HF_TOKEN)
358
+
359
+ # Rebuild annotations dictionary from dataset
360
+ for item in dataset:
361
+ user_id = item["user_id"]
362
+ if user_id not in self.annotations:
363
+ self.annotations[user_id] = []
364
+
365
+ # Add to user's annotations
366
+ self.annotations[user_id].append({
367
+ "user_id": user_id,
368
+ "sample_id": item["sample_id"],
369
+ "choice": item["choice"],
370
+ "model_a": item.get("model_a", ""),
371
+ "model_b": item.get("model_b", ""),
372
+ "timestamp": item["timestamp"]
373
+ })
374
+
375
+ # Update user state
376
+ if user_id not in self.user_states:
377
+ self.user_states[user_id] = {
378
+ "current_index": 0,
379
+ "annotations": []
380
+ }
381
+ if item["sample_id"] not in self.user_states[user_id]["annotations"]:
382
+ self.user_states[user_id]["annotations"].append(item["sample_id"])
383
+
384
+ print(f"Loaded {len(dataset)} existing annotations")
385
+
386
+ except Exception as e:
387
+ print(f"Error loading existing annotations: {e}")
388
+ print("Starting with empty annotations")
389
+
390
  def get_user_seed(self, user_id: str) -> int:
391
  """Generate consistent seed for user"""
392
+ return int(hashlib.md5(user_id.encode()).hexdigest(), 16) % 10000
393
+
394
  def get_user_samples(self, user_id: str) -> List[Dict]:
395
  """Get shuffled samples for user based on their ID"""
396
  seed = self.get_user_seed(user_id)
397
+ samples = DATASET_SAMPLES.copy()
398
  random.Random(seed).shuffle(samples)
 
 
 
 
399
  return samples
400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  def get_next_sample(self, user_id: str) -> Tuple[Dict, int, int]:
402
  """Get next unannotated sample for user"""
403
  if user_id not in self.user_states:
 
409
  samples = self.get_user_samples(user_id)
410
  state = self.user_states[user_id]
411
 
412
+ # Count already annotated
413
+ annotated_count = len(state["annotations"])
414
+
415
  # Find next unannotated sample
416
+ for i, sample in enumerate(samples):
 
417
  if not self.is_annotated(user_id, sample["id"]):
418
+ return sample, annotated_count + 1, len(samples)
 
419
 
420
  # All samples annotated
421
  return None, len(samples), len(samples)
 
425
  if user_id not in self.annotations:
426
  return False
427
  return any(ann["sample_id"] == sample_id for ann in self.annotations[user_id])
428
+
429
+ def save_annotation(self, user_id: str, sample_id: str, choice: str,
430
+ sample_data: Dict = None):
431
+ """Save user's annotation locally and to HuggingFace"""
432
+ with self.lock:
433
+ if user_id not in self.annotations:
434
+ self.annotations[user_id] = []
435
+
436
+ annotation = {
437
+ "user_id": user_id,
438
+ "sample_id": sample_id,
439
+ "choice": choice,
440
+ "timestamp": datetime.now().isoformat()
441
+ }
442
+
443
+ # Add sample data if provided
444
+ if sample_data:
445
+ annotation.update({
446
+ "original_id": sample_data.get("original_id", ""),
447
+ "model_a": sample_data.get("model_a", ""),
448
+ "model_b": sample_data.get("model_b", ""),
449
+ "prompt": sample_data.get("prompt", ""),
450
+ "response_a": sample_data.get("response_a", ""),
451
+ "response_b": sample_data.get("response_b", ""),
452
+ "dataset": sample_data.get("dataset", "")
453
+ })
454
+
455
+ self.annotations[user_id].append(annotation)
456
+
457
+ # Update user state
458
+ if user_id in self.user_states:
459
+ if sample_id not in self.user_states[user_id]["annotations"]:
460
+ self.user_states[user_id]["annotations"].append(sample_id)
461
+ self.user_states[user_id]["current_index"] += 1
462
+
463
+ print(f"Saved annotation locally: {annotation['sample_id']} by {user_id}")
464
+
465
+ # Save to HuggingFace asynchronously
466
+ if HF_TOKEN:
467
+ thread = threading.Thread(
468
+ target=self.push_annotation_to_hub,
469
+ args=(annotation,)
470
+ )
471
+ thread.daemon = True
472
+ thread.start()
473
+
474
+ def push_annotation_to_hub(self, annotation: Dict):
475
+ """Push single annotation to HuggingFace dataset"""
476
+ try:
477
+ # Load current dataset
478
+ dataset = load_dataset(OUTPUT_DATASET_NAME, split="train", token=HF_TOKEN)
479
+
480
+ # Convert to dict
481
+ data_dict = dataset.to_dict()
482
+
483
+ # Ensure all keys exist
484
+ required_keys = ["user_id", "sample_id", "original_id", "model_a",
485
+ "model_b", "choice", "prompt", "response_a",
486
+ "response_b", "dataset", "timestamp"]
487
+
488
+ for key in required_keys:
489
+ if key not in data_dict:
490
+ data_dict[key] = []
491
+ # Append new annotation data
492
+ data_dict[key].append(annotation.get(key, ""))
493
+
494
+ # Create new dataset and push
495
+ updated_dataset = Dataset.from_dict(data_dict)
496
+ updated_dataset.push_to_hub(
497
+ OUTPUT_DATASET_NAME,
498
+ token=HF_TOKEN,
499
+ private=OUTPUT_DATASET_PRIVATE
500
+ )
501
+
502
+ print(f"Successfully pushed annotation to hub: {annotation['sample_id']}")
503
+
504
+ except Exception as e:
505
+ print(f"Error pushing annotation to hub: {e}")
506
+ # Add to cache for batch upload later
507
+ self.annotation_cache.append(annotation)
508
+
509
+ def get_user_progress(self, user_id: str) -> Dict:
510
+ """Get user's annotation progress"""
511
+ if user_id not in self.user_states:
512
+ return {"completed": 0, "total": len(DATASET_SAMPLES)}
513
+
514
+ completed = len(self.user_states[user_id]["annotations"])
515
+ return {"completed": completed, "total": len(DATASET_SAMPLES)}
516
 
517
 
518
  # Initialize manager
 
555
  gr.update(value=sample["prompt"]), # prompt
556
  gr.update(value=sample["response_a"]), # response_a
557
  gr.update(value=sample["response_b"]), # response_b
558
+ gr.update(value=f"Progress: {current}/{total} | Comparing: {sample.get('model_a', 'A')} vs {sample.get('model_b', 'B')}") # progress
559
+ # gr.update(value=f"Progress: {current}/{total}") # progress
560
  )
561
 
562
  def annotate(choice: str, user_id: str) -> Tuple:
 
579
  "b_better": "B is more fluent",
580
  "equal": "Equally fluent"
581
  }
582
+
583
+ # Save with full sample data for HuggingFace dataset
584
  manager.save_annotation(
585
  user_id,
586
  sample["id"],
587
  choice_map[choice],
588
+ sample_data=sample # Pass the full sample data
 
589
  )
590
 
591
  # Get next sample
 
600
  gr.update(value="All annotations complete!", visible=True) # status
601
  )
602
 
603
+ # Show which models are being compared
604
+ model_info = f" | Comparing: {next_sample.get('model_a', 'A')} vs {next_sample.get('model_b', 'B')}"
605
+
606
  return (
607
  gr.update(value=next_sample["prompt"]), # prompt
608
  gr.update(value=next_sample["response_a"]), # response_a
609
  gr.update(value=next_sample["response_b"]), # response_b
610
+ gr.update(value=f"Progress: {current}/{total} | Comparing: {sample.get('model_a', 'A')} vs {sample.get('model_b', 'B')}"),
611
+ # gr.update(value=f"Progress: {current}/{total}{model_info}"), # progress
612
  gr.update(value="Annotation saved!", visible=True) # status
613
  )
614