Pandago/graphstrike-model-training
0
1from __future__ import annotations2 3from enum import Enum4from typing import Dict, List, Optional5 6from pydantic import BaseModel7 8# ---------------------------------------------------------------------------9# OpenEnv base types10# Use real SDK when available; fall back to stubs for local dev without SDK.11# ---------------------------------------------------------------------------12try:13 from openenv.core.env_server import Action, Observation, State # type: ignore14except ImportError:15 class Action(BaseModel): # type: ignore[no-redef]16 pass17 18 class Observation(BaseModel): # type: ignore[no-redef]19 done: bool = False20 reward: Optional[float] = None21 22 class State(BaseModel): # type: ignore[no-redef]23 episode_id: str = ""24 step_count: int = 025 26# ---------------------------------------------------------------------------27# Domain types28# ---------------------------------------------------------------------------29 30class ActionType(str, Enum):31 INSPECT = "inspect" # reveal full profile + edges, costs 1 step32 INVESTIGATE_NETWORK = "investigate_network" # expand graph 1 hop, costs 2 steps33 FLAG = "flag" # mark account fake (free)34 UNFLAG = "unflag" # unmark account (free)35 SUBMIT = "submit" # end episode, trigger scoring36 # Round 2: New tool-call actions37 REVERSE_IMAGE_SEARCH = "reverse_image_search" # reveal photo_reuse_score, costs 1 step38 ANALYZE_BIO = "analyze_bio" # reveal bio_template_score, costs 1 step39 CHECK_IP = "check_ip" # reveal ip_cluster_signal, costs 2 steps40 GET_POLICY = "get_policy" # get platform policy, costs 0 steps41 42 43class AccountStatus(str, Enum):44 NORMAL = "normal"45 SUSPECT = "suspect" # auto-elevated when a neighbor is flagged46 CONFIRMED_FAKE = "confirmed_fake" # agent explicitly flagged this account47 48 49class FakeGangAction(Action):50 action_type: ActionType51 account_id: Optional[str] = None # required for all actions except SUBMIT52 53 54class AccountProfile(BaseModel):55 account_id: str56 follower_count: int57 following_count: int58 post_count: int59 avg_post_hour: float # 0–2360 photo_reuse_score: float # 0–1 — pre-computed: fraction of posts using stolen celebrity photos61 bio_template_score: float # 0–1 — pre-computed: cosine similarity to known fake bio templates62 account_age_days: int63 name_change_count: int = 0 # incremented by hard-mode evasion events64 65 # ── Derived graph features (computed at INSPECT time from live graph state) ──66 flagged_neighbor_count: int = 0 # how many of this account's follows are currently flagged67 # high value = deep inside a cluster you're already tracking68 mutual_follow_rate: float = 0.0 # fraction of follows that also follow back (0–1)69 # real fans: low; fake gangs: high (they mutually inflate each other)70 avg_neighbor_photo_reuse: float = 0.0 # mean photo_reuse_score of inspected follows71 # gang members cluster: if neighbors are fake, this is high72 73 visible_follows: List[str] = [] # IDs of accounts this account follows (revealed by INSPECT)74 75 # ── Account status ──76 status: AccountStatus = AccountStatus.NORMAL77 78 # ── Full risk breakdown (computed via scoring.py at INSPECT time) ──79 fake_risk_score: float = 0.080 node_risk: float = 0.081 behavior_risk: float = 0.082 graph_risk: float = 0.083 hub_legitimacy_score: float = 0.084 85 # ── New raw features (from generator) ──86 comment_repeat_score: float = 0.0 # fakes: 0.6-0.9 | decoys: 0.1-0.3 | reals: 0.0-0.0887 shared_ip_count: int = 0 # fakes: 9 (gang shares 1 IP) | reals: 0-188 89 # ── Extended runtime graph features ──90 inspected_neighbor_count: int = 0 # denominator for flagged_neighbor_ratio91 post_hour_cluster_score: float = 0.0 # hour alignment to flagged cluster mean92 suspicious_mutual_ratio: float = 0.0 # used in hub legitimacy computation93 94 95class FakeGangObservation(Observation):96 visible_accounts: List[AccountProfile] = []97 visible_account_ids: List[str] = [] # all account IDs the agent knows exist98 flagged_ids: List[str] = []99 inspected_ids: List[str] = []100 graph_edges: Dict[str, List[str]] = {} # account_id -> list of accounts it follows101 steps_remaining: int = 0102 evasion_triggered: bool = False103 evasion_count: int = 0104 task: str = "easy"105 message: str = ""106 suspect_ids: List[str] = [] # auto-elevated neighbors of flagged accounts107 platform: str = "" # Round 2: Platform name (Instagram/Snapchat) - passed from state108 109 110class FakeGangState(State):111 task: str = "easy"112 score_so_far: float = 0.0113 evasion_count: int = 0114 network_size: int = 0115 gang_size: int = 10116 episode_seed: int = 0117 platform: str = "" # Round 2: Platform name (Instagram/Snapchat)118 119 120# ---------------------------------------------------------------------------121# Round 2: Platform Policy Model122# ---------------------------------------------------------------------------123 124class PlatformPolicy(BaseModel):125 """Dynamically compiled platform policy from transparency reports."""126 platform: str # "Instagram" or "Snapchat"127 threshold: float # θ* - computed Bayesian threshold for flagging128 base_rate: float # π - prevalence of fake accounts129 fn_cost_signal: str # "low" | "medium" | "high" | "critical"130 fp_cost_signal: str # "low" | "medium" | "high"131 harm_weight: float # enforcement vs creator balance (0.5-2.0)132 primary_enforcement_signal: str # "photo_reuse" | "bio_template" | "ip_cluster"133 fp_penalty_weight: float # C_fp for reward function134 sources: List[str] = [] # URLs used for extraction135 confidence: float = 0.0 # LLM extraction confidence (0.0-1.0)136 compiled_at: str = "" # ISO timestamp137 used_fallback: bool = False # True if fallback policy was used due to extraction failure138 