rmi-backend/app/_archive/legacy_2026_07/intel_pipeline.py
cryptorugmunch 628c1d2a10
Some checks failed
CI / build (push) Failing after 3s
refactor(rmi-backend,audit): mount Wave 3 + archive 136 dead-code files (P2.3)
PHASE 2.3 (AUDIT-2026-Q3.md):

Task 1 — Wire-in Wave 3 (1 router mounted, 2 deferred):
  - app.routers.unified_scanner_router mounted at /api/v2/scanner/* (2 routes:
    POST /api/v2/scanner/token/scan, POST /api/v2/scanner/wallet/scan).
    Refactored prefix from /api/v2 -> /api/v2/scanner to avoid future conflicts
    with the v1 /api/v1/scanner/ stub.
  - app.routers.unified_wallet_scanner DEFERRED (no router APIRouter attribute;
    library module consumed by unified_scanner_router via get_wallet_scanner()).
  - app.routers.admin_extensions DEFERRED (DORMANT per audit; 25 routes at
    /api/v1/admin/* would shadow /api/v1/admin/alerts_webhook).

Task 2 — Archive 136 dead-code files to app/_archive/legacy_2026_07/:
  - 73 routers in app/routers/ (reach graph showed zero reach into mount.py).
  - 63 flat app/*.py (domain modules never imported by live code).
  - 1 file RESTORED post-archive: app/routers/x402_bridge_health.py (caught by
    tests/unit/test_bridge_health.py which directly imports it; reach graph
    considered tests/ only as transitive reach — to be patched in next cycle).

Forced-LIVE (NOT archived per user directive):
  - app/ai_pipeline_v3.py  (3 importers in audit window, importers themselves DEAD)
  - app/splade_bm25.py       (LIVE via app.rag_service)
  - app/wallet_manager_v2.py (LIVE via x402_enforcement, x402_tools, sweep_all, sweep_now)
  - app/crypto_embeddings.py (NOT in audit ARCHIVE list; heavy import graph)

Verification (forward-import closure from mount.py + main.py + factory.py + lifespan.py):
  - imports = 348 app.* modules
  - reached = 194 files reachable from roots
  - archive set = audit_dead (186) - reached - forced_live (4) - test_live (1) = 136
  - Net delta: 136 files moved, 44,932 LOC reduction, 293->295 active routes (+2 from Wave 3)

pyproject.toml updates:
  - setuptools.packages.find: added exclude for app._archive*
  - ruff.extend-exclude: added "app/_archive/"
  - mypy.exclude: added "app/_archive/"

Smoke test: pytest tests/ — 817 passed, 3 pre-existing failures unchanged
(0 new failures; 0 routes lost; all 4 forced-LIVE files still importable).

Restoration: git mv app/_archive/legacy_2026_07/<name>.py <original-path>
and add the import to app/mount.py ROUTER_MODULES.

Refs: AUDIT-2026-Q3.md /home/dev/pry/rmi-final-deadcode-2026-07-06.md
2026-07-06 20:52:31 +02:00

298 lines
9.8 KiB
Python

"""
RMI Intelligence Pipeline - HF + Supabase + RAG
================================================
Unified intelligence service: scam classification (HF models),
wallet labeling via RAG pattern matching, Supabase hybrid storage.
"""
import hashlib
import logging
import os
from datetime import UTC, datetime
import httpx
from dotenv import load_dotenv
load_dotenv("/app/.env", override=True)
logger = logging.getLogger(__name__)
HF_TOKEN = os.getenv("HF_TOKEN", "")
HF_API = "https://api-inference.huggingface.co/models"
def _get_url():
return os.getenv("SUPABASE_URL", "")
def _get_key():
return os.getenv("SUPABASE_SERVICE_KEY", "")
def _get_headers():
key = _get_key()
return {
"apikey": key,
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
}
# ── HF Models (Paywalled - using local fallback) ──────
# HF Inference API now requires PRO subscription ($9/mo).
# Using local pattern matching + RAG memory instead.
# Enable HF by setting HUGGINGFACE_TOKEN to a valid PRO key.
SCAM_LABELS = ["scam", "rugpull", "honeypot", "legitimate", "phishing", "ponzi"]
SCAM_KEYWORDS = {
"scam": ["scam", "fraud", "stole", "stolen", "exit scam", "fake"],
"rugpull": ["rug", "rugpull", "pulled liquidity", "drained", "removed liquidity", "lp removed"],
"honeypot": [
"honeypot",
"cannot sell",
"can't sell",
"unable to sell",
"transfer disabled",
"sell tax 100",
],
"phishing": [
"phishing",
"airdrop scam",
"claim reward",
"verify wallet",
"seed phrase",
"private key",
],
"ponzi": [
"ponzi",
"mlm",
"multi level",
"referral rewards",
"guaranteed returns",
"double your",
],
"insider": ["insider", "team wallet", "dev wallet", "pre-sale", "unlocked tokens", "vesting"],
}
async def classify_scam_risk(text: str) -> dict:
"""Classify scam risk using local pattern matching (HF paywalled)."""
text_lower = text.lower()
scores = {}
for label, keywords in SCAM_KEYWORDS.items():
score = sum(1 for kw in keywords if kw in text_lower)
if score > 0:
scores[label] = min(score / len(keywords), 1.0)
try:
from app.rag_service import search_documents
rag_results = await search_documents("known_scams", text, limit=3)
for r in rag_results:
content = r.get("content", "").lower()
for label, keywords in SCAM_KEYWORDS.items():
if any(kw in content for kw in keywords):
scores[label] = scores.get(label, 0) + 0.1
except Exception:
pass
if not scores:
return {
"risk": "low",
"labels": {},
"confidence": 0,
"is_scam": False,
"risk_level": "low",
"model": "local_pattern_match",
}
top = max(scores, key=scores.get)
top_score = scores[top]
is_scam = top in ("scam", "rugpull", "honeypot", "phishing", "ponzi")
return {
"model": "local_pattern_match",
"labels": {k: round(v, 3) for k, v in sorted(scores.items(), key=lambda x: x[1], reverse=True)},
"top_label": top,
"confidence": round(top_score, 3),
"is_scam": is_scam,
"risk_level": "high" if (is_scam and top_score > 0.5) else "medium" if is_scam else "low",
}
async def generate_embedding(text: str) -> list[float] | None:
"""Generate embedding vector for RAG storage using HF free tier."""
if not HF_TOKEN:
return None
async with httpx.AsyncClient(timeout=60) as client:
r = await client.post(
f"{HF_API}/{EMBEDDING_MODEL}", # noqa: F821 -- pre-existing bug, see fix(f821) tracking issue
headers={"Authorization": f"Bearer {HF_TOKEN}"},
json={"inputs": text[:1024]},
)
if r.status_code == 503:
logger.warning("HF embedding cold start, model loading")
return None
if r.status_code == 200:
result = r.json()
# Handle different response formats
if isinstance(result, list) and len(result) > 0:
return result[0] if isinstance(result[0], list) else result
return result
logger.warning(f"HF embedding failed: {r.status_code}")
return None
# ── Wallet Labeling via Pattern Memory ──────────────────
WALLET_PATTERNS = {
"sybil_farmer": [
"funded from exchange within seconds of 100+ other wallets",
"identical funding amounts across multiple wallets",
"no organic activity, only test transactions",
"funded by known Sybil distributor",
],
"wash_trader": [
"circular transfers between related wallets",
"buys and sells same token within minutes",
"volume spikes without holder count changes",
"coordinated buy/sell patterns",
],
"sandwich_bot": [
"high frequency trading with frontrun pattern",
"buys before large buys, sells immediately after",
"MEV extraction patterns",
"flashbots bundle usage",
],
"liquidity_remover": [
"large LP removal shortly after token launch",
"multiple LP positions removed simultaneously",
"liquidity drained to fresh wallet",
"LP removal preceded by marketing push",
],
"honeypot_deployer": [
"deploys tokens that can't be sold",
"reuses contract code across multiple tokens",
"disables transfers after liquidity added",
"ownership not renounced, hidden mint functions",
],
"phishing_operator": [
"sends tokens to many addresses with scam links",
"impersonates legitimate token contracts",
"uses airdrop as phishing vector",
"connects to known phishing domains",
],
"mixer_user": [
"funds pass through Tornado Cash or similar",
"receives from mixer, sends to clean wallet",
"layered mixing through multiple hops",
"funds originate from high-risk sources",
],
"insider_trader": [
"buys tokens before public announcements",
"linked to team wallets or deployers",
"sells immediately after hype peak",
"coordinated timing with other insiders",
],
}
async def label_wallet(wallet_data: dict) -> dict:
"""Label a wallet based on behavioral patterns and RAG memory."""
labels = []
confidence_scores = {}
# Check behavioral patterns
behavior = wallet_data.get("behavior_summary", "")
wallet_data.get("transactions", [])
for label, patterns in WALLET_PATTERNS.items():
score = 0
for pattern in patterns:
if pattern.lower() in behavior.lower():
score += 1
if score > 0:
confidence = min(score / len(patterns), 1.0)
confidence_scores[label] = round(confidence, 2)
if confidence > 0.3:
labels.append({"label": label, "confidence": round(confidence, 2)})
# Query RAG for similar wallet patterns
try:
from app.rag_service import search_documents
rag_results = await search_documents(
"wallet_profiles", wallet_data.get("address", "") + " " + behavior, limit=5
)
if rag_results:
for r in rag_results:
content = r.get("content", "")
for label, patterns in WALLET_PATTERNS.items():
if any(p.lower() in content.lower() for p in patterns):
if label not in confidence_scores:
confidence_scores[label] = 0
confidence_scores[label] = min(confidence_scores[label] + 0.15, 1.0)
except Exception:
pass
labels = [
{"label": k, "confidence": v}
for k, v in sorted(confidence_scores.items(), key=lambda x: x[1], reverse=True)
if v > 0.2
]
return {
"wallet_address": wallet_data.get("address", "unknown"),
"labels": labels[:5],
"primary_label": labels[0]["label"] if labels else "unknown",
"risk_score": max([line["confidence"] for line in labels]) * 100 if labels else 0,
"analyzed_at": datetime.now(UTC).isoformat(),
}
# ── Supabase Hybrid Storage ────────────────────────────
async def sync_to_supabase(collection: str, document: dict) -> dict:
"""Sync RAG document to Supabase for persistent hybrid storage."""
if not _get_url() or not _get_key():
return {"status": "skipped", "reason": "No Supabase config"}
doc_id = hashlib.sha256(f"{collection}:{document.get('content', '')[:100]}".encode()).hexdigest()[:16]
payload = {
"document_id": doc_id,
"collection": collection,
"content": document.get("content", "")[:5000],
"metadata": document.get("metadata", {}),
"synced_at": datetime.now(UTC).isoformat(),
}
headers = _get_headers()
headers["Prefer"] = "resolution=merge-duplicates"
async with httpx.AsyncClient(timeout=15) as client:
r = await client.post(f"{_get_url()}/rest/v1/rag_documents", json=payload, headers=headers)
if r.status_code in (200, 201, 409):
return {"status": "synced", "doc_id": doc_id, "supabase_status": r.status_code}
return {"status": "failed", "error": r.text[:200]}
# ── Batch Processing ───────────────────────────────────
async def run_intelligence_cycle() -> dict:
"""Run a full intelligence cycle: classify, label, embed, sync."""
results = {
"cycle": datetime.now(UTC).isoformat(),
"scam_checks": 0,
"wallet_labels": 0,
"embeddings": 0,
"supabase_syncs": 0,
"errors": [],
}
return results