pliny-the-prompter commited on
Commit
0bec1ea
·
verified ·
1 Parent(s): 9c94816

Upload 133 files

Browse files
Files changed (2) hide show
  1. app.py +37 -13
  2. obliteratus/tourney.py +33 -4
app.py CHANGED
@@ -3143,6 +3143,33 @@ def _tourney_gpu_run(fn, *args, **kwargs):
3143
  return fn(*args, **kwargs)
3144
 
3145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3146
  def run_tourney(model_choice, dataset, quantization):
3147
  """Run an elimination tournament across all abliteration methods.
3148
 
@@ -3169,10 +3196,7 @@ def run_tourney(model_choice, dataset, quantization):
3169
 
3170
  quant = quantization if quantization != "none" else None
3171
 
3172
- log_lines: list[str] = []
3173
-
3174
- def on_log(msg):
3175
- log_lines.append(msg)
3176
 
3177
  dataset_key = get_source_key_from_label(dataset) if dataset else "builtin"
3178
 
@@ -3191,7 +3215,7 @@ def run_tourney(model_choice, dataset, quantization):
3191
  hub_repo=None,
3192
  dataset_key=dataset_key,
3193
  quantization=quant,
3194
- on_log=on_log,
3195
  resume=resume,
3196
  )
3197
  except Exception as e:
@@ -3218,16 +3242,16 @@ def run_tourney(model_choice, dataset, quantization):
3218
 
3219
  result = None
3220
  try:
3221
- for status_msg, partial_result in runner.run_iter(gpu_wrapper=_tourney_gpu_run):
3222
  result = partial_result
3223
  yield (
3224
  status_msg,
3225
  "",
3226
- "\n".join(log_lines[-100:]),
3227
  )
3228
  except Exception as e:
3229
  tb = traceback.format_exc()
3230
- log_lines.append(f"\nTRACEBACK:\n{tb}")
3231
  if _is_quota_error(e):
3232
  # Provide a helpful message for ZeroGPU quota exhaustion
3233
  bracket_md = ""
@@ -3242,18 +3266,18 @@ def run_tourney(model_choice, dataset, quantization):
3242
  "HuggingFace Pro subscribers get 7x more daily quota.\n\n"
3243
  "**Tip:** use quantization to reduce per-method GPU time.",
3244
  bracket_md,
3245
- "\n".join(log_lines),
3246
  )
3247
  else:
3248
  yield (
3249
  f"**Error:** {type(e).__name__}: {e}",
3250
  "",
3251
- "\n".join(log_lines),
3252
  )
3253
  return
3254
 
3255
  if not result:
3256
- yield ("**Error:** Tournament produced no result.", "", "\n".join(log_lines))
3257
  return
3258
 
3259
  winner = result.winner
@@ -3306,7 +3330,7 @@ def run_tourney(model_choice, dataset, quantization):
3306
  f"(score: {winner.score:.4f})\n"
3307
  f"Push it to HuggingFace Hub from the **Push to Hub** tab.",
3308
  bracket_md,
3309
- "\n".join(log_lines),
3310
  )
3311
  else:
3312
  n_errors = sum(
@@ -3320,7 +3344,7 @@ def run_tourney(model_choice, dataset, quantization):
3320
  yield (
3321
  msg,
3322
  bracket_md,
3323
- "\n".join(log_lines),
3324
  )
3325
 
3326
 
 
3143
  return fn(*args, **kwargs)
3144
 
3145
 
3146
+ class _TourneyLogger:
3147
+ """Picklable log collector for tournament progress.
3148
+
3149
+ Gradio's queue system pickles generator frames, so closures like
3150
+ ``lambda msg: log_lines.append(msg)`` cause PicklingError. This
3151
+ simple class is picklable and serves the same purpose.
3152
+ """
3153
+
3154
+ def __init__(self):
3155
+ self.lines: list[str] = []
3156
+
3157
+ def __call__(self, msg: str):
3158
+ self.lines.append(msg)
3159
+
3160
+ def tail(self, n: int = 100) -> str:
3161
+ """Return the last *n* log lines joined by newlines. ``n=0`` returns all."""
3162
+ if n <= 0:
3163
+ return "\n".join(self.lines)
3164
+ return "\n".join(self.lines[-n:])
3165
+
3166
+
3167
+ def _tourney_gpu_wrapper(fn, *args, **kwargs):
3168
+ """Indirection so the @spaces.GPU-wrapped function is resolved at call
3169
+ time rather than captured in the generator frame (which Gradio pickles)."""
3170
+ return _tourney_gpu_run(fn, *args, **kwargs)
3171
+
3172
+
3173
  def run_tourney(model_choice, dataset, quantization):
3174
  """Run an elimination tournament across all abliteration methods.
3175
 
 
3196
 
3197
  quant = quantization if quantization != "none" else None
3198
 
3199
+ logger = _TourneyLogger()
 
 
 
3200
 
3201
  dataset_key = get_source_key_from_label(dataset) if dataset else "builtin"
3202
 
 
3215
  hub_repo=None,
3216
  dataset_key=dataset_key,
3217
  quantization=quant,
3218
+ on_log=logger,
3219
  resume=resume,
3220
  )
3221
  except Exception as e:
 
3242
 
3243
  result = None
3244
  try:
3245
+ for status_msg, partial_result in runner.run_iter(gpu_wrapper=_tourney_gpu_wrapper):
3246
  result = partial_result
3247
  yield (
3248
  status_msg,
3249
  "",
3250
+ logger.tail(),
3251
  )
3252
  except Exception as e:
3253
  tb = traceback.format_exc()
3254
+ logger(f"\nTRACEBACK:\n{tb}")
3255
  if _is_quota_error(e):
3256
  # Provide a helpful message for ZeroGPU quota exhaustion
3257
  bracket_md = ""
 
3266
  "HuggingFace Pro subscribers get 7x more daily quota.\n\n"
3267
  "**Tip:** use quantization to reduce per-method GPU time.",
3268
  bracket_md,
3269
+ logger.tail(0),
3270
  )
3271
  else:
3272
  yield (
3273
  f"**Error:** {type(e).__name__}: {e}",
3274
  "",
3275
+ logger.tail(0),
3276
  )
3277
  return
3278
 
3279
  if not result:
3280
+ yield ("**Error:** Tournament produced no result.", "", logger.tail(0))
3281
  return
3282
 
3283
  winner = result.winner
 
3330
  f"(score: {winner.score:.4f})\n"
3331
  f"Push it to HuggingFace Hub from the **Push to Hub** tab.",
3332
  bracket_md,
3333
+ logger.tail(0),
3334
  )
3335
  else:
3336
  n_errors = sum(
 
3344
  yield (
3345
  msg,
3346
  bracket_md,
3347
+ logger.tail(0),
3348
  )
3349
 
3350
 
obliteratus/tourney.py CHANGED
@@ -727,6 +727,33 @@ tokenizer = AutoTokenizer.from_pretrained("{result.hub_repo or 'this-repo'}")
727
  """
728
 
729
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  # ---------------------------------------------------------------------------
731
  # Core runner
732
  # ---------------------------------------------------------------------------
@@ -773,8 +800,8 @@ class TourneyRunner:
773
  if self.output_dir.exists():
774
  shutil.rmtree(self.output_dir, ignore_errors=True)
775
  self.output_dir.mkdir(parents=True, exist_ok=True)
776
- self._on_log = on_log or (lambda msg: None)
777
- self._on_round = on_round or (lambda r: None)
778
 
779
  def log(self, msg: str):
780
  self._on_log(msg)
@@ -801,6 +828,8 @@ class TourneyRunner:
801
 
802
  try:
803
  # Use informed pipeline for 'informed' method
 
 
804
  if method == "informed":
805
  from obliteratus.informed_pipeline import InformedAbliterationPipeline
806
  pipeline = InformedAbliterationPipeline(
@@ -812,7 +841,7 @@ class TourneyRunner:
812
  trust_remote_code=True,
813
  harmful_prompts=harmful,
814
  harmless_prompts=harmless,
815
- on_log=lambda msg: self.log(f" [{method}] {msg}"),
816
  )
817
  pipeline.run_informed()
818
  else:
@@ -828,7 +857,7 @@ class TourneyRunner:
828
  harmful_prompts=harmful,
829
  harmless_prompts=harmless,
830
  verify_sample_size=verify_sample_size,
831
- on_log=lambda msg: self.log(f" [{method}] {msg}"),
832
  )
833
  pipeline.run()
834
 
 
727
  """
728
 
729
 
730
+ def _noop_log(msg: str) -> None:
731
+ """Picklable no-op log callback (lambdas can't be pickled by ZeroGPU)."""
732
+ pass
733
+
734
+
735
+ def _noop_round(r: TourneyRound) -> None:
736
+ """Picklable no-op round callback."""
737
+ pass
738
+
739
+
740
+ class _MethodLogger:
741
+ """Picklable per-method log adapter that prefixes messages.
742
+
743
+ ZeroGPU pickles bound methods (and their ``self``) when shipping work to
744
+ the GPU worker process. Plain lambdas like
745
+ ``lambda msg: self.log(f" [{method}] {msg}")`` can't survive that, so
746
+ this small class replaces them.
747
+ """
748
+
749
+ def __init__(self, parent_log: Callable[[str], None], method: str):
750
+ self._parent = parent_log
751
+ self._method = method
752
+
753
+ def __call__(self, msg: str):
754
+ self._parent(f" [{self._method}] {msg}")
755
+
756
+
757
  # ---------------------------------------------------------------------------
758
  # Core runner
759
  # ---------------------------------------------------------------------------
 
800
  if self.output_dir.exists():
801
  shutil.rmtree(self.output_dir, ignore_errors=True)
802
  self.output_dir.mkdir(parents=True, exist_ok=True)
803
+ self._on_log = on_log or _noop_log
804
+ self._on_round = on_round or _noop_round
805
 
806
  def log(self, msg: str):
807
  self._on_log(msg)
 
828
 
829
  try:
830
  # Use informed pipeline for 'informed' method
831
+ method_log = _MethodLogger(self._on_log, method)
832
+
833
  if method == "informed":
834
  from obliteratus.informed_pipeline import InformedAbliterationPipeline
835
  pipeline = InformedAbliterationPipeline(
 
841
  trust_remote_code=True,
842
  harmful_prompts=harmful,
843
  harmless_prompts=harmless,
844
+ on_log=method_log,
845
  )
846
  pipeline.run_informed()
847
  else:
 
857
  harmful_prompts=harmful,
858
  harmless_prompts=harmless,
859
  verify_sample_size=verify_sample_size,
860
+ on_log=method_log,
861
  )
862
  pipeline.run()
863