Capture uncensored-Qwen-35B automation (others hung via API)

The uncensored Qwen 35B hangs/timeouts on 4/5 prompts via the API
(only automation saved). qwen3.6-27b and glm-4.7-flash also time out
via API (27B genuinely slow ~12min; GLM loops without stopping — needs
stop-token handling, noted for next session). Capture batch paused:
3 models fully captured (Gemma26B, kat-coder, qwen3-coder-30b),
grading deferred.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-28 20:21:13 -07:00
co-authored by Claude
parent 0c79a997f5
commit c98b6bb257
2 changed files with 290 additions and 5 deletions
@@ -0,0 +1,285 @@
#!/usr/bin/env python3
"""
Async Batch Processor with Checkpointing, Idempotency & Graceful Interruption.
Runs unattended, re-runnable automation glue. Uses only Python 3.11+ stdlib.
"""
from __future__ import annotations
import asyncio
import json
import os
import random
import signal
import sys
import tempfile
import time
from typing import Any, Dict, List, Optional, Tuple
class ProcessingError(Exception):
"""Raised when the external call fails."""
pass
async def process(item: str) -> str:
"""
Simulates a flaky external API call.
- Latency: 10-80ms random uniform
- Failure rate: ~20% (raises ProcessingError)
"""
await asyncio.sleep(random.uniform(0.01, 0.08))
if random.random() < 0.2:
raise ProcessingError(f"Flaky timeout/network error for {item}")
return f"result-{item}"
# --------------------------------------------------------------------------- #
# Checkpoint I/O
# --------------------------------------------------------------------------- #
def load_checkpoint(path: str) -> Dict[str, Any]:
"""Load checkpoint or return empty structure if missing."""
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
return {"items": {}, "summary": {"succeeded": 0, "failed": 0}}
def save_checkpoint(
items_dict: Dict[str, str], succeeded: int, failed: int, path: str
) -> None:
"""Atomically write checkpoint using temp file + rename."""
data = {
"items": items_dict,
"summary": {"succeeded": succeeded, "failed": failed},
}
dir_path = os.path.dirname(os.path.abspath(path)) or "."
fd, tmp_path = tempfile.mkstemp(dir=dir_path, suffix=".tmp")
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
json.dump(data, f)
os.replace(tmp_path, path) # Atomic on POSIX & Windows (Vista+)
except Exception:
if os.path.exists(tmp_path):
os.unlink(tmp_path)
raise
# --------------------------------------------------------------------------- #
# Core Processor
# --------------------------------------------------------------------------- #
async def run_batch(
items: List[str],
checkpoint_path: str = "checkpoint.json",
concurrency_limit: int = 8,
stop_event: Optional[asyncio.Event] = None,
) -> Tuple[Dict[str, Any], int]:
"""
Process a list of items with bounded concurrency, retries, and checkpointing.
Returns:
(summary_dict, max_concurrent_tasks_observed)
"""
if stop_event is None:
stop_event = asyncio.Event()
checkpoint = load_checkpoint(checkpoint_path)
completed_items: Dict[str, str] = checkpoint.get("items", {})
semaphore = asyncio.Semaphore(concurrency_limit)
running_tasks: List[asyncio.Task[Any]] = []
succeeded_count = checkpoint["summary"]["succeeded"]
failed_count = checkpoint["summary"]["failed"]
skipped_count = 0
max_concurrent = 0
current_concurrent = 0
start_time = time.monotonic()
async def process_item(item: str) -> None:
nonlocal succeeded_count, failed_count, max_concurrent, current_concurrent
async with semaphore:
current_concurrent += 1
if current_concurrent > max_concurrent:
max_concurrent = current_concurrent
try:
# Retry up to 3 times on ProcessingError (4 attempts total)
for attempt in range(4):
try:
await process(item)
completed_items[item] = "success"
succeeded_count += 1
save_checkpoint(completed_items, succeeded_count, failed_count, checkpoint_path)
break # Success, exit retry loop
except ProcessingError:
if attempt == 3: # Last attempt exhausted
completed_items[item] = "failed"
failed_count += 1
save_checkpoint(completed_items, succeeded_count, failed_count, checkpoint_path)
else:
backoff = 0.1 * (2 ** attempt) # 0.1, 0.2, 0.4
await asyncio.sleep(backoff)
finally:
current_concurrent -= 1
for item in items:
if stop_event.is_set():
break
status = completed_items.get(item)
if status in ("success", "failed"):
skipped_count += 1
continue
task = asyncio.create_task(process_item(item))
running_tasks.append(task)
# Let in-flight tasks finish before exiting
if stop_event.is_set():
await asyncio.gather(*running_tasks, return_exceptions=True)
elapsed_ms = int((time.monotonic() - start_time) * 1000)
summary = {
"succeeded": succeeded_count,
"failed": failed_count,
"skipped": skipped_count,
"total": len(items),
"elapsed_ms": elapsed_ms,
}
print(json.dumps(summary))
return summary, max_concurrent
# --------------------------------------------------------------------------- #
# Test Suite
# --------------------------------------------------------------------------- #
async def test_resumability() -> None:
"""a) Run batch, kill mid-run, restart. Assert no reprocessing & terminal state."""
print("Running Test A: Resumability & Idempotency...")
items = [f"resum-{i}" for i in range(20)]
cp_path = "test_checkpoint_a.json"
# First run: cancel after 150ms to simulate SIGINT/crash mid-flight
async def run_and_timeout():
await asyncio.wait_for(run_batch(items, checkpoint_path=cp_path), timeout=0.15)
task = asyncio.create_task(run_and_timeout())
try:
await task
except asyncio.TimeoutError:
pass
# Allow cancellation to propagate and in-flight tasks to flush checkpoints
await asyncio.sleep(0.2)
# Second run (resume)
summary, _ = await run_batch(items, checkpoint_path=cp_path)
assert summary["total"] == 20
assert summary["succeeded"] + summary["failed"] + summary["skipped"] == 20
# Verify all items are terminal in checkpoint
with open(cp_path) as f:
cp = json.load(f)
for item in items:
assert cp["items"][item] in ("success", "failed"), \
f"{item} is not terminal: {cp['items'][item]}"
# Verify no completed item was reprocessed (skipped count == previously finished)
with open(cp_path) as f:
final_cp = json.load(f)
expected_skipped = sum(1 for v in final_cp["items"].values() if v in ("success", "failed"))
assert summary["skipped"] == expected_skipped, \
f"Reprocessing detected. Expected skipped={expected_skipped}, got {summary['skipped']}"
print(" PASSED")
async def test_checkpoint_integrity() -> None:
"""b) Watcher reads checkpoint repeatedly during run, asserts valid JSON always."""
print("Running Test B: Checkpoint Integrity...")
items = [f"integ-{i}" for i in range(10)]
cp_path = "test_checkpoint_b.json"
integrity_ok = True
async def watcher():
nonlocal integrity_ok
while True:
if os.path.exists(cp_path):
try:
with open(cp_path, "r", encoding="utf-8") as f:
json.load(f) # Raises on invalid JSON
except (json.JSONDecodeError, FileNotFoundError):
integrity_ok = False
return
await asyncio.sleep(0.05)
watcher_task = asyncio.create_task(watcher())
await run_batch(items, checkpoint_path=cp_path)
try:
await asyncio.wait_for(watcher_task, timeout=1.0)
except asyncio.TimeoutError:
pass
assert integrity_ok, "Checkpoint contained invalid JSON at some point during execution"
print(" PASSED")
async def test_concurrency_cap() -> None:
"""c) Assert concurrency cap of 8 holds."""
print("Running Test C: Concurrency Cap...")
items = [f"conc-{i}" for i in range(50)]
cp_path = "test_checkpoint_c.json"
_, max_conc = await run_batch(items, checkpoint_path=cp_path)
assert max_conc <= 8, f"Concurrency cap violated: observed {max_conc} > 8"
print(" PASSED")
async def main_tests() -> None:
"""Run all tests sequentially."""
try:
await test_resumability()
await test_checkpoint_integrity()
await test_concurrency_cap()
print("\n✅ All tests passed.")
except AssertionError as e:
print(f"\n❌ Test failed: {e}")
sys.exit(1)
# --------------------------------------------------------------------------- #
# Entry Point
# --------------------------------------------------------------------------- #
if __name__ == "__main__":
# If run with --batch, execute the processor normally. Otherwise run tests.
if "--batch" in sys.argv:
items = [f"job-{i}" for i in range(50)] # Example workload
loop = asyncio.get_running_loop()
stop_event = asyncio.Event()
def handle_sigint():
stop_event.set()
old_handler = signal.signal(signal.SIGINT, lambda s, f: loop.call_soon_threadsafe(handle_sigint))
try:
asyncio.run(run_batch(items, stop_event=stop_event))
except KeyboardInterrupt:
pass # Handled by stop_event + gather
finally:
signal.signal(signal.SIGINT, old_handler)
else:
asyncio.run(main_tests())