Spaces:
Sleeping
Sleeping
Download inference.py from Rohith9059/gdpr: direct link, hf CLI and curl.
- Browser
- Download file 8.58 kB
-
https://huggingface.co/spaces/Rohith9059/gdpr/resolve/main/inference.py
- Command line
-
hf download hf://spaces/Rohith9059/gdpr/inference.py
-
curl -L -o inference.py https://huggingface.co/spaces/Rohith9059/gdpr/resolve/main/inference.py
8.58 kB
| from __future__ import annotations | |
| import json | |
| import os | |
| import sys | |
| import time | |
| from typing import Any, Dict, List | |
| import requests | |
| from openai import OpenAI | |
| from pydantic import ValidationError | |
| from models import EraseOpsAction | |
| API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1") | |
| MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct") | |
| HF_TOKEN = os.getenv("HF_TOKEN") | |
| TASK_NAME = os.getenv("TASK_NAME", "clean_sweep") | |
| ENV_URL = os.getenv("ENV_URL", "http://localhost:7860") | |
| MAX_TOTAL_RUNTIME_SECONDS = int(os.getenv("MAX_TOTAL_RUNTIME_SECONDS", "1100")) | |
| MAX_TASK_RUNTIME_SECONDS = int(os.getenv("MAX_TASK_RUNTIME_SECONDS", "260")) | |
| SYSTEM_PROMPT = ( | |
| "You are a Data Privacy Officer agent managing GDPR Right-to-Erasure requests.\n" | |
| "At each step you receive a JSON observation showing all data records across enterprise systems.\n" | |
| "Your goal: delete all PII records belonging to the target user_id in the request.\n\n" | |
| "Rules you must follow:\n" | |
| "1. NEVER delete or anonymize records where legal_hold=True - escalate these instead\n" | |
| "2. NEVER delete records belonging to a different user_id than in the request\n" | |
| "3. Always delete dependency records BEFORE the records that depend on them\n" | |
| "4. Use 'inspect' action first if you are unsure about a record's dependencies\n" | |
| "5. Use 'anonymize' only for derived or anonymized type records, never for PII\n\n" | |
| "Respond with ONLY a valid JSON object matching this schema:\n" | |
| "{\n" | |
| " 'action_type': 'inspect' | 'delete' | 'anonymize' | 'escalate' | 'noop',\n" | |
| " 'record_id': 'rec_001',\n" | |
| " 'justification': 'brief reason'\n" | |
| "}" | |
| ) | |
| client = OpenAI(api_key=HF_TOKEN, base_url=API_BASE_URL) | |
| def _log_start(task: str, model: str) -> None: | |
| print(f"[START] task={task} env=gdpr-eraseops model={model}", flush=True) | |
| def _log_step(step: int, action_json: str, reward: float, done: bool, error: str | None) -> None: | |
| error_text = "null" if error in (None, "", "None") else error | |
| print( | |
| f"[STEP] step={step} action={action_json} reward={reward:.2f} done={str(done).lower()} error={error_text}", | |
| flush=True, | |
| ) | |
| def _log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None: | |
| rewards_str = ",".join(f"{value:.2f}" for value in rewards) | |
| print(f"[END] success={str(success).lower()} steps={steps} score={score:.2f} rewards={rewards_str}", flush=True) | |
| def _safe_action() -> Dict[str, Any]: | |
| return {"action_type": "noop", "record_id": None, "justification": "safe fallback"} | |
| def _candidate_record(observation: Dict[str, Any]) -> str | None: | |
| request_user = observation.get("request", {}).get("user_id") | |
| records = observation.get("records", []) | |
| for record in records: | |
| if record.get("status") != "active": | |
| continue | |
| if record.get("legal_hold") and record.get("user_id") == request_user: | |
| return str(record.get("record_id")) | |
| for record in records: | |
| if record.get("status") != "active": | |
| continue | |
| if record.get("user_id") != request_user: | |
| continue | |
| if record.get("data_type") != "PII": | |
| continue | |
| dependencies = record.get("dependencies", []) | |
| deps_deleted = True | |
| for dep_id in dependencies: | |
| dep = next((r for r in records if r.get("record_id") == dep_id), None) | |
| if dep is None or dep.get("status") != "deleted": | |
| deps_deleted = False | |
| break | |
| if deps_deleted: | |
| return str(record.get("record_id")) | |
| return None | |
| def _heuristic_action(observation: Dict[str, Any]) -> Dict[str, Any]: | |
| request_user = observation.get("request", {}).get("user_id") | |
| records = observation.get("records", []) | |
| step = int(observation.get("step", 0) or 0) | |
| if step < min(3, len(records)): | |
| rec = records[step] | |
| return { | |
| "action_type": "inspect", | |
| "record_id": rec.get("record_id"), | |
| "justification": "progressive reconnaissance", | |
| } | |
| for record in records: | |
| if ( | |
| record.get("status") == "active" | |
| and record.get("user_id") == request_user | |
| and record.get("legal_hold") is True | |
| ): | |
| return { | |
| "action_type": "escalate", | |
| "record_id": record.get("record_id"), | |
| "justification": "legal hold discovered", | |
| } | |
| candidate = _candidate_record(observation) | |
| if candidate: | |
| return { | |
| "action_type": "delete", | |
| "record_id": candidate, | |
| "justification": "delete active in-scope PII with dependencies satisfied", | |
| } | |
| return {"action_type": "noop", "record_id": None, "justification": "no safe move"} | |
| def _llm_action(observation: Dict[str, Any]) -> Dict[str, Any]: | |
| if not HF_TOKEN: | |
| return _heuristic_action(observation) | |
| user_prompt = ( | |
| f"{json.dumps(observation, separators=(',', ':'), ensure_ascii=False)}\n" | |
| "Choose your next action. Respond with JSON only." | |
| ) | |
| try: | |
| response = client.chat.completions.create( | |
| model=MODEL_NAME, | |
| messages=[ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": user_prompt}, | |
| ], | |
| temperature=0.0, | |
| max_tokens=220, | |
| ) | |
| raw = (response.choices[0].message.content or "").strip() | |
| if raw.startswith("```"): | |
| raw = raw.replace("```json", "").replace("```", "").strip() | |
| parsed = json.loads(raw) | |
| return { | |
| "action_type": parsed.get("action_type", "noop"), | |
| "record_id": parsed.get("record_id"), | |
| "justification": parsed.get("justification"), | |
| } | |
| except Exception: | |
| return _heuristic_action(observation) | |
| def _validated_action(raw_action: Dict[str, Any]) -> Dict[str, Any]: | |
| try: | |
| return EraseOpsAction(**raw_action).model_dump() | |
| except ValidationError: | |
| return _safe_action() | |
| def _run_task(task: str) -> float: | |
| _log_start(task, MODEL_NAME) | |
| task_started_at = time.monotonic() | |
| reset_resp = requests.post( | |
| f"{ENV_URL}/reset", | |
| params={"task_name": task, "seed": 42}, | |
| timeout=20, | |
| ) | |
| reset_resp.raise_for_status() | |
| observation = reset_resp.json() | |
| rewards: List[float] = [] | |
| done = False | |
| step = 0 | |
| max_steps = int(observation.get("step_budget", 30) or 30) | |
| while not done and step < max_steps: | |
| if time.monotonic() - task_started_at > MAX_TASK_RUNTIME_SECONDS: | |
| print(f"Runtime guard: task '{task}' timed out", file=sys.stderr, flush=True) | |
| done = True | |
| break | |
| step += 1 | |
| raw_action = _llm_action(observation) | |
| action = _validated_action(raw_action) | |
| action_json = json.dumps(action, separators=(",", ":"), ensure_ascii=False) | |
| error_text: str | None = None | |
| reward_total = 0.0 | |
| try: | |
| step_resp = requests.post(f"{ENV_URL}/step", json=action, timeout=20) | |
| step_resp.raise_for_status() | |
| payload = step_resp.json() | |
| observation = payload.get("observation", {}) | |
| reward_total = float(payload.get("reward", {}).get("total", 0.0) or 0.0) | |
| done = bool(payload.get("done", False)) | |
| info = payload.get("info", {}) | |
| error_text = info.get("error") if isinstance(info, dict) else None | |
| except Exception as exc: | |
| done = True | |
| reward_total = -1.0 | |
| error_text = str(exc) | |
| rewards.append(reward_total) | |
| _log_step(step=step, action_json=action_json, reward=reward_total, done=done, error=error_text) | |
| grade_resp = requests.post(f"{ENV_URL}/grade", params={"task_name": task}, timeout=20) | |
| grade_resp.raise_for_status() | |
| score = float(grade_resp.json().get("score", 0.0) or 0.0) | |
| success = score >= 0.5 | |
| _log_end(success=success, steps=step, score=score, rewards=rewards) | |
| return score | |
| def main() -> None: | |
| """Run the full baseline across all tasks.""" | |
| tasks = ["clean_sweep", "tangled_lake", "legal_minefield", "cascade_collapse"] | |
| run_started_at = time.monotonic() | |
| for task in tasks: | |
| if time.monotonic() - run_started_at > MAX_TOTAL_RUNTIME_SECONDS: | |
| print("Runtime guard: stopping before timeout", file=sys.stderr, flush=True) | |
| break | |
| _run_task(task) | |
| if __name__ == "__main__": | |
| main() | |