refactor(domains): move 15 app-root monoliths into app/domains/ + lazy cloudscraper

This commit is contained in:
Crypto Rug Munch 2026-07-07 22:00:39 +07:00
parent 25e0891a0d
commit 27184c704d
63 changed files with 145 additions and 136 deletions

View file

@ -411,7 +411,7 @@ async def auto_label_cluster(
Auto-label a cluster by comparing its behavioral vector to known templates.
Uses cosine similarity between cluster behavior and template descriptions.
"""
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
embedder = await get_embedder()
signals = cluster.get("signals", cluster.get("behavior_signals", []))
@ -540,7 +540,7 @@ async def search_clusters_by_description(
"Show me all wash trading clusters from Solana in the last month"
embeds the query, searches against cluster behavioral vectors
"""
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
from app.supabase_vector import get_vector_store
embedder = await get_embedder()
@ -677,7 +677,7 @@ All labels: {json.dumps(labels["all_labels"])}"""
async def backfill_label_templates():
"""Index all cluster label templates into pgvector for auto-labeling."""
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
from app.supabase_vector import get_vector_store
embedder = await get_embedder()

View file

@ -241,7 +241,7 @@ async def generate_briefing() -> dict[str, Any]:
# Pull from RAG
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
for topic, query in [
("Scam Alerts", "rug pull honeypot scam today"),
@ -275,7 +275,7 @@ async def generate_newsletter() -> dict[str, Any]:
content_parts = [f"# 📰 Weekly Intelligence Report\n\n*Week of {datetime.now(UTC).strftime('%B %d, %Y')}*\n"]
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
for section, query in [
("🔴 Top Threats", "major scam exploit hack this week"),

View file

@ -17,7 +17,7 @@ from pydantic import BaseModel, Field
# Add the app directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from app.degen_security_scanner import (
from app.domains.scanners.degen_security_scanner import (
DegenSecurityScanner,
SecurityReport,
scan_token,

View file

@ -17,9 +17,6 @@ from app.domains.billing.x402.config import (
SECURITY_HEADERS,
SOL_PAY_TO,
TOOL_PRICES,
_ensure_pay_addresses,
_get_pay_to_address,
_resolve_asset,
)
from app.domains.billing.x402.idempotency import check_trial

View file

@ -462,7 +462,7 @@ class DataBus:
# ── 9. RAG INDEXING (optional) ──
if rag_index and result.get("data"):
try:
from app.rag_service import index_document
from app.domains.intelligence.rag_service import index_document
doc_id = f"databus:{data_type}:{hash(str(kwargs))}"
await index_document(

View file

@ -610,7 +610,7 @@ async def aggregate_all_news(limit: int = 50, **kw) -> dict:
# Internal sources
try:
from app.news_service import fetch_all_news
from app.domains.news.news_service import fetch_all_news
tasks.append(fetch_all_news())
except Exception:

View file

@ -251,7 +251,7 @@ async def get_market_brief(**kw) -> dict | None:
async def get_aggregated_news(limit: int = 20, category: str = "", **kw) -> dict | None:
"""Pull news from our existing news_service.py aggregator."""
try:
from app.news_service import fetch_all_news
from app.domains.news.news_service import fetch_all_news
articles = await fetch_all_news()
if articles:

View file

@ -87,7 +87,7 @@ async def classify_scam_risk(text: str) -> dict:
scores[label] = min(score / len(keywords), 1.0)
try:
from app.rag_service import search_documents
from app.domains.intelligence.rag_service import search_documents
rag_results = await search_documents("known_scams", text, limit=3)
for r in rag_results:
@ -222,7 +222,7 @@ async def label_wallet(wallet_data: dict) -> dict:
# Query RAG for similar wallet patterns
try:
from app.rag_service import search_documents
from app.domains.intelligence.rag_service import search_documents
rag_results = await search_documents(
"wallet_profiles", wallet_data.get("address", "") + " " + behavior, limit=5

View file

@ -21,7 +21,7 @@ import re
from datetime import UTC, datetime
from typing import Any
from app.crypto_embeddings import (
from app.domains.intelligence.crypto_embeddings import (
COLLECTIONS,
KNOWN_SCAM_PATTERNS,
CryptoEmbedder,

View file

@ -296,7 +296,7 @@ class AddressLabeler:
"""Query internal RAG service for known scam and wallet profile data."""
entries = []
try:
from app.rag_service import RAGService
from app.domains.intelligence.rag_service import RAGService
rag = RAGService()

View file

@ -32,7 +32,7 @@ Competitive advantage:
that single-step detectors miss
Usage:
from app.flash_loan_attack_detector import FlashLoanAttackDetector
from app.domains.scanners.flash_loan_attack_detector import FlashLoanAttackDetector
detector = FlashLoanAttackDetector()
report = await detector.scan(blocks_back=50)

View file

@ -22,7 +22,7 @@ Standalone usage:
python3 liquidation_cascade_analyzer.py <wallet_address> --chains ethereum,base,arbitrum
API usage:
from app.liquidation_cascade_analyzer import LiquidationCascadeAnalyzer
from app.domains.scanners.liquidation_cascade_analyzer import LiquidationCascadeAnalyzer
analyzer = LiquidationCascadeAnalyzer()
result = await analyzer.analyze("0x...")
print(result.report())

View file

@ -25,7 +25,7 @@ Competitive advantage:
- Uses DataBus for on-chain data and tx_simulator for impact assessment
Usage:
from app.mev_sandwich_detector import MEVSandwichDetector
from app.domains.scanners.mev_sandwich_detector import MEVSandwichDetector
detector = MEVSandwichDetector()
report = await detector.scan()

View file

@ -33,7 +33,7 @@ Competitive advantage:
that single-block price checkers miss
Usage:
from app.oracle_manipulation_detector import OracleManipulationDetector
from app.domains.scanners.oracle_manipulation_detector import OracleManipulationDetector
detector = OracleManipulationDetector()
report = await detector.scan(blocks_back=50)

View file

@ -21,7 +21,7 @@ Standalone usage:
python3 portfolio_risk_aggregator.py <wallet_address> --chains ethereum,base,solana
API usage:
from app.portfolio_risk_aggregator import PortfolioRiskAggregator
from app.domains.scanners.portfolio_risk_aggregator import PortfolioRiskAggregator
agg = PortfolioRiskAggregator()
profile = await agg.analyze("0x...")
print(profile.report())
@ -665,7 +665,7 @@ class TokenRiskScorer:
try:
import importlib
self._scanner_available = importlib.util.find_spec("app.unified_scanner") is not None
self._scanner_available = importlib.util.find_spec("app.domains.scanners.unified_scanner") is not None
except ImportError:
pass
@ -676,7 +676,7 @@ class TokenRiskScorer:
if self._scanner_available:
try:
from app.unified_scanner import scan_token as _scan_token
from app.domains.scanners.unified_scanner import scan_token as _scan_token
result = await _scan_token(chain, address)
if result:

View file

@ -31,7 +31,7 @@ Competitive advantage:
- RAG integration for known pump group wallet signatures
Usage:
from app.pump_dump_manipulation_detector import PumpDumpDetector
from app.domains.scanners.pump_dump_manipulation_detector import PumpDumpDetector
detector = PumpDumpDetector()
result = await detector.analyze_token("0x...", chain="ethereum")
@ -40,8 +40,8 @@ Usage:
print(f" [{finding.severity}] {finding.description}")
CLI:
python3 -m app.pump_dump_manipulation_detector 0x... --chain ethereum
python3 -m app.pump_dump_manipulation_detector 0x... --chain solana --alert
python3 -m app.domains.scanners.pump_dump_manipulation_detector 0x... --chain ethereum
python3 -m app.domains.scanners.pump_dump_manipulation_detector 0x... --chain solana --alert
"""
import asyncio

View file

@ -94,7 +94,7 @@ async def query_rag_citations(
source, title, collection, similarity, snippet, reference
"""
try:
from app.rag_service import search_multi_collection
from app.domains.intelligence.rag_service import search_multi_collection
except ImportError:
logger.debug("RAG service not available, skipping citations")
return []
@ -177,7 +177,7 @@ async def query_address_rag(
is already known in our RAG databases.
"""
try:
from app.rag_service import search_multi_collection
from app.domains.intelligence.rag_service import search_multi_collection
except ImportError:
return []

View file

@ -25,7 +25,7 @@ Competitive differentiator:
- Real-time alerting via webhook for monitored tokens
Usage:
from app.rug_imminence_predictor import RugImminencePredictor
from app.domains.scanners.rug_imminence_predictor import RugImminencePredictor
predictor = RugImminencePredictor()
result = await predictor.predict("0x1234...", chain="base")
print(result.score, result.verdict, result.narrative())

View file

@ -29,7 +29,7 @@ Competitive advantage:
- Uses multiple data sources with fallback for maximum coverage
Usage:
from app.smart_contract_honeypot_detector import HoneypotDetector
from app.domains.scanners.smart_contract_honeypot_detector import HoneypotDetector
detector = HoneypotDetector()
result = await detector.analyze_token("0x...", chain="ethereum")

View file

@ -738,7 +738,7 @@ async def hybrid_query(
vector_docs - docs from vector search
"""
# Late import to avoid circular dependency at module level
from app.rag_service import search_multi_collection
from app.domains.intelligence.rag_service import search_multi_collection
# 1. Extract entities from query
extraction = extract_entities(query)

View file

@ -12,8 +12,6 @@ import logging
import time
from typing import Any
import cloudscraper
logger = logging.getLogger(__name__)
# ─── Token Generator ──────────────────────────────────────────────────────────
@ -29,11 +27,23 @@ def generate_solauth_token() -> str:
# ─── Scrapers (separate instances for parallelism) ────────────────────────────
_scraper = cloudscraper.create_scraper(browser={"browser": "chrome", "platform": "windows", "desktop": True})
_scraper: Any | None = None
BASE_URL = "https://api-v2.solscan.io/v2"
def _get_scraper() -> Any:
"""Lazy cloudscraper creation so the module can import without it installed."""
global _scraper
if _scraper is None:
import cloudscraper
_scraper = cloudscraper.create_scraper(
browser={"browser": "chrome", "platform": "windows", "desktop": True}
)
return _scraper
def _headers() -> dict[str, str]:
return {
"Accept": "application/json, text/plain, */*",
@ -52,7 +62,7 @@ def _request(endpoint: str, params: dict | None = None, retries: int = 3) -> Any
url = f"{BASE_URL}{endpoint}"
for attempt in range(retries):
try:
response = _scraper.get(url, headers=_headers(), params=params or {}, timeout=15)
response = _get_scraper().get(url, headers=_headers(), params=params or {}, timeout=15)
if response.status_code == 200:
data = response.json()
if isinstance(data, dict) and "data" in data:

View file

@ -413,8 +413,8 @@ class IntelPipeline:
async def run_cycle(self) -> dict[str, Any]:
"""Run one full ingestion cycle across all sources."""
from app.crypto_embeddings import get_embedder
from app.rag_service import _get_redis
from app.domains.intelligence.crypto_embeddings import get_embedder
from app.domains.intelligence.rag_service import _get_redis
embedder = await get_embedder()
r = await _get_redis()
@ -561,7 +561,7 @@ class IntelPipeline:
async def get_latest_intel(self, limit: int = 20) -> list[dict[str, Any]]:
"""Get latest threat intelligence for frontend display."""
from app.rag_service import _get_redis
from app.domains.intelligence.rag_service import _get_redis
r = await _get_redis()

View file

@ -169,7 +169,7 @@ async def _execute_hop(
hop_name = hop.get("hop", "unknown")
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
results = await three_pillar_search(
query=hop_query,

View file

@ -363,7 +363,7 @@ async def _check_historical_mev(chain: str, token_address: str | None = None) ->
score = 0.2
try:
# Try to import the MEV detector for historical data
from app.mev_sandwich_detector import MEVSandwichDetector
from app.domains.scanners.mev_sandwich_detector import MEVSandwichDetector
detector = MEVSandwichDetector()
stats = await detector.scan()

View file

@ -21,7 +21,7 @@ async def check_health() -> dict:
# Check RAG
try:
from app.rag_service import search_similar
from app.domains.intelligence.rag_service import search_similar
await search_similar("health_check", "known_scams", limit=1, min_similarity=0.1)
except Exception as e:
@ -31,7 +31,7 @@ async def check_health() -> dict:
# Check scanner
try:
from app.degen_security_scanner import DegenSecurityScanner
from app.domains.scanners.degen_security_scanner import DegenSecurityScanner
scanner = DegenSecurityScanner()
await scanner.scan_token("health_check", "solana")

View file

@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
# Attempt imports - these are stubs and may not exist
try:
from app.rag_service import detect_scam_patterns, search_similar
from app.domains.intelligence.rag_service import detect_scam_patterns, search_similar
except ImportError:
detect_scam_patterns = None
search_similar = None

View file

@ -3,7 +3,7 @@ from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Optional, List
from app.rag_service import RAGService
from app.domains.intelligence.rag_service import RAGService
from app.scanner import Scanner
from app.blocklist import Blocklist
from app.wallet_labels import WalletLabels

View file

@ -248,7 +248,7 @@ Return as JSON. Be precise and evidence-based."""
Multi-hop investigation pipeline.
Returns structured findings with evidence trail.
"""
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
embedder = await get_embedder()
context = context or {}
@ -360,7 +360,7 @@ Return as JSON. Be precise and evidence-based."""
embedder,
) -> dict:
"""Execute a single investigation hop."""
from app.rag_service import search_multi_collection, search_similar
from app.domains.intelligence.rag_service import search_multi_collection, search_similar
action = step["action"]
query = step["query"]
@ -474,7 +474,7 @@ async def stream_rag_search(
Streaming RAG search - yields results as they're discovered.
Pattern: "thinking..." "found N matches..." "reranking..." results
"""
from app.rag_service import search_multi_collection, search_similar
from app.domains.intelligence.rag_service import search_multi_collection, search_similar
# Phase 1: Quick keyword pre-filter (instant)
yield json.dumps({"phase": "quick_scan", "status": "searching"}) + "\n"
@ -553,7 +553,7 @@ class RealTimeTokenMonitor:
async def scan_new_token(self, token_data: dict) -> dict[str, Any]:
"""Quick-look scan of a newly deployed token."""
from app.rag_service import detect_scam_patterns
from app.domains.intelligence.rag_service import detect_scam_patterns
address = token_data.get("address", "")
if address in self.processed:

View file

@ -233,7 +233,7 @@ def content_hash(text: str) -> str:
async def is_duplicate(collection: str, text: str, redis_client=None) -> bool:
"""Check if content already exists in the collection via hash dedup."""
if redis_client is None:
from app.rag_service import _get_redis
from app.domains.intelligence.rag_service import _get_redis
redis_client = await _get_redis()
@ -251,7 +251,7 @@ async def mark_ingested(
) -> None:
"""Mark content as ingested in dedup registry."""
if redis_client is None:
from app.rag_service import _get_redis
from app.domains.intelligence.rag_service import _get_redis
redis_client = await _get_redis()

View file

@ -119,7 +119,7 @@ async def snapshot_all():
# Get collections from rag_service (Redis-backed)
try:
from app.rag_service import COLLECTIONS, _get_redis
from app.domains.intelligence.rag_service import COLLECTIONS, _get_redis
redis = await _get_redis()
has_redis = True
@ -241,7 +241,7 @@ async def restore_all():
"""Restore all RAG collections from latest R2 snapshots."""
# Get collections and Redis connection
try:
from app.rag_service import COLLECTIONS, _get_redis
from app.domains.intelligence.rag_service import COLLECTIONS, _get_redis
redis = await _get_redis()
except Exception:
@ -336,7 +336,7 @@ async def r2_stats():
"""Get R2 usage + local cache stats."""
# Get collections and Redis connection
try:
from app.rag_service import COLLECTIONS, _get_redis
from app.domains.intelligence.rag_service import COLLECTIONS, _get_redis
redis = await _get_redis()
has_redis = True

View file

@ -584,7 +584,7 @@ async def answer_relevancy(answer: str, query: str) -> float:
return 0.0
try:
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
embedder = await get_embedder()
@ -670,7 +670,7 @@ async def run_evaluation(
Returns EvaluationReport with per-query and aggregate metrics.
"""
from app.rag_service import search_similar
from app.domains.intelligence.rag_service import search_similar
# Filter test set by collection if specified
test_cases = GOLDEN_TEST_SET

View file

@ -1,5 +1,6 @@
"""Auto-Healing Infrastructure - self-diagnosing, self-repairing platform."""
import logging
import os
from datetime import UTC, datetime
@ -7,6 +8,7 @@ import httpx
import psutil
from fastapi import APIRouter
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1/health", tags=["auto-healing"])
BACKEND = os.getenv("BACKEND_URL", "http://localhost:8000")

View file

@ -18,7 +18,7 @@ import os
from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field
from app.multichain_airdrop import (
from app.domains.scanners.multichain_airdrop import (
CrossChainIdentityResolver,
MultiChainAirdropManager,
TokenSource,
@ -220,7 +220,7 @@ async def create_crm_preset(request: Request, body: CRMPresetRequest, _=Depends(
"""
try:
if body.mode == "one_to_one":
from app.multichain_airdrop import create_crm_v2_airdrop_preset
from app.domains.scanners.multichain_airdrop import create_crm_v2_airdrop_preset
campaign = await create_crm_v2_airdrop_preset(
crm_solana_address=body.crm_solana_address,
@ -276,7 +276,7 @@ async def execute_snapshot(request: Request, body: ExecuteSnapshotRequest, _=Dep
try:
# Use custom snapshot if provided
if body.use_custom_snapshot:
from app.multichain_airdrop import CustomSnapshotLoader
from app.domains.scanners.multichain_airdrop import CustomSnapshotLoader
if body.custom_snapshot_format == "json" and body.custom_snapshot_data:
holders = CustomSnapshotLoader.parse_json_snapshot(body.custom_snapshot_data)
@ -324,7 +324,7 @@ async def execute_snapshot(request: Request, body: ExecuteSnapshotRequest, _=Dep
async def upload_snapshot(request: Request, body: UploadSnapshotRequest, _=Depends(_verify_admin)):
"""Upload a custom snapshot file (JSON or CSV) for a campaign."""
try:
from app.multichain_airdrop import CustomSnapshotLoader
from app.domains.scanners.multichain_airdrop import CustomSnapshotLoader
if body.format == "json":
holders = CustomSnapshotLoader.parse_json_snapshot(body.data)
@ -362,7 +362,7 @@ async def upload_snapshot(request: Request, body: UploadSnapshotRequest, _=Depen
async def add_manual_holders(request: Request, body: ManualHoldersRequest, _=Depends(_verify_admin)):
"""Add manual holders to a campaign (or replace existing)."""
try:
from app.multichain_airdrop import CustomSnapshotLoader
from app.domains.scanners.multichain_airdrop import CustomSnapshotLoader
# Parse manual holders
new_holders = CustomSnapshotLoader.parse_manual_holders(body.holders)
@ -470,7 +470,7 @@ async def full_multichain_launch(request: Request, body: CRMv2LaunchRequest, _=D
await storage.save(deployment)
# 2. Create 1:1 airdrop campaign
from app.multichain_airdrop import create_crm_v2_airdrop_preset
from app.domains.scanners.multichain_airdrop import create_crm_v2_airdrop_preset
campaign = await create_crm_v2_airdrop_preset(
crm_solana_address=body.crm_solana_address,
@ -608,7 +608,7 @@ async def create_tiered_campaign(request: Request, body: CreateTieredCampaignReq
for s in body.source_tokens
]
from app.multichain_airdrop import AirdropTier
from app.domains.scanners.multichain_airdrop import AirdropTier
tiers = [
AirdropTier(

View file

@ -98,7 +98,7 @@ async def _refresh_cache(include_rss: bool, include_reddit: bool, include_intern
"""Background cache refresh task."""
global _NEWS_CACHE, _CACHE_TS
try:
from app.news_service import get_news_service
from app.domains.news.news_service import get_news_service
svc = get_news_service()
result = await svc.fetch_all(
@ -147,7 +147,7 @@ async def get_news_feed(
else:
# First load or forced refresh - fetch inline but with limits
try:
from app.news_service import get_news_service
from app.domains.news.news_service import get_news_service
svc = get_news_service()
result = await svc.fetch_all(

View file

@ -101,7 +101,7 @@ async def check_rag_for_url(url: str) -> dict[str, Any] | None:
"""
try:
# Import RAG service lazily to avoid circular deps
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
# Search in known_scams and scam_patterns collections for the URL
# Add timeout to prevent slow RAG from blocking endpoint
@ -213,7 +213,7 @@ async def scan_url_content(url: str) -> dict[str, Any] | None:
async def feed_url_to_rag(url: str, is_safe: bool):
"""Feed URL scan result back to RAG for learning (best effort)."""
try:
from app.rag_service import ingest_document
from app.domains.intelligence.rag_service import ingest_document
doc = {
"content": f"URL: {url}\\nSafety: {'safe' if is_safe else 'unsafe'}\\nScanned by RMI Protection Suite",
@ -311,7 +311,7 @@ async def check_wallet(request: WalletCheckRequest, http_request: Request):
chain = "ethereum" # default fallback
# Use unified scanner for wallet risk with timeout
from app.unified_scanner import scan_wallet as wallet_scan
from app.domains.scanners.unified_scanner import scan_wallet as wallet_scan
# We'll use the free tier scan with a timeout of 1.8s to prevent slow scans
try:
@ -479,7 +479,7 @@ async def get_domains():
# Extract domains from RAG known_scams collection
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
# Query RAG for known scams (look for URLs in content)
rag_results = await asyncio.wait_for(
@ -559,7 +559,7 @@ async def protection_health():
# Check RAG (simple ping)
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
# We'll do a quick query
await three_pillar_search(query="test", collections=["known_scams"], limit=1, min_similarity=0.5)

View file

@ -555,7 +555,7 @@ async def portfolio_history(wallet: str, chain: str = "ethereum", hours: int = 2
@router.post("/monitor/watch")
async def monitor_watch(address: str, chain: str = "ethereum", tags: str = ""):
"""Add a wallet to the watchlist."""
_wm = _L("app.wallet_monitor")
_wm = _L("app.domains.wallet.wallet_monitor")
manager = _wm.WalletMonitorManager()
await manager.add_watch(address, chain, tags.split(",") if tags else [])
return {"watched": True, "address": address, "chain": chain}
@ -564,7 +564,7 @@ async def monitor_watch(address: str, chain: str = "ethereum", tags: str = ""):
@router.post("/monitor/unwatch")
async def monitor_unwatch(address: str, chain: str = "ethereum"):
"""Remove a wallet from the watchlist."""
_wm = _L("app.wallet_monitor")
_wm = _L("app.domains.wallet.wallet_monitor")
manager = _wm.WalletMonitorManager()
await manager.remove_watch(address, chain)
return {"unwatched": True, "address": address, "chain": chain}
@ -573,7 +573,7 @@ async def monitor_unwatch(address: str, chain: str = "ethereum"):
@router.get("/monitor/watches")
async def monitor_list():
"""List all watched wallets."""
_wm = _L("app.wallet_monitor")
_wm = _L("app.domains.wallet.wallet_monitor")
manager = _wm.WalletMonitorManager()
watches = await manager.list_watches()
return {"watches": watches, "count": len(watches)}

View file

@ -481,7 +481,7 @@ async def rug_probability(req: AddressRequest):
try:
import asyncio as _asyncio
from app.rag_service import detect_scam_patterns
from app.domains.intelligence.rag_service import detect_scam_patterns
result = _asyncio.run(detect_scam_patterns({"address": addr, "chain": chain}, 0.4))
if result and result.get("risk_score", 0) > 0:

View file

@ -349,7 +349,7 @@ def search_similar_patterns(
patterns = []
try:
from app.rag_service import get_embedder, search_multi_collection
from app.domains.intelligence.rag_service import get_embedder, search_multi_collection
embedder = get_embedder()
if not embedder:
@ -495,7 +495,7 @@ def enrich_tool_response(
try:
import asyncio
from app.rag_service import detect_scam_patterns
from app.domains.intelligence.rag_service import detect_scam_patterns
for addr in addresses[:5]:
try:

View file

@ -193,7 +193,7 @@ async def reputation_score(req: AddressRequest):
try:
import asyncio
from app.rag_service import detect_scam_patterns
from app.domains.intelligence.rag_service import detect_scam_patterns
result = asyncio.run(detect_scam_patterns({"address": addr, "chain": chain}, 0.5))
if result and result.get("risk_score", 0) > 0:

View file

@ -92,7 +92,7 @@ async def search_similar_scams(token_address: str, chain: str) -> list[dict]:
similar = []
try:
from app.ann_index import get_ann_index
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
ann = get_ann_index()
embedder = CryptoEmbedder()

View file

@ -354,7 +354,7 @@ class ScamIngestionPipeline:
"last_run": self._last_run.isoformat(),
}
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
embedder = await get_embedder()
@ -365,7 +365,7 @@ class ScamIngestionPipeline:
docs = await ingest_fn(embedder)
if docs:
# Ingest via Redis + FAISS (pgvector removed)
from app.rag_service import ingest_document
from app.domains.intelligence.rag_service import ingest_document
count = 0
for doc in docs:

View file

@ -96,7 +96,7 @@ async def _run_watch_scans(watch_id: str):
try:
# 1. Run unified scanner if available
try:
from app.unified_scanner import scan_wallet
from app.domains.scanners.unified_scanner import scan_wallet
result = await scan_wallet(token_address, chain=chain)
_push_update(
@ -118,7 +118,7 @@ async def _run_watch_scans(watch_id: str):
# 2. RAG search for known scam patterns
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
rag_results = await three_pillar_search(
query=f"{token_address} scam detection",

View file

@ -273,7 +273,7 @@ class SupabaseVectorStore:
if not self._table_ready:
# Fallback to Redis
try:
from app.rag_service import _get_redis
from app.domains.intelligence.rag_service import _get_redis
r = await _get_redis()
doc = {
@ -352,8 +352,8 @@ class SupabaseVectorStore:
# Fallback to Redis
if not self._table_ready:
try:
from app.crypto_embeddings import get_embedder
from app.rag_service import _get_redis
from app.domains.intelligence.crypto_embeddings import get_embedder
from app.domains.intelligence.rag_service import _get_redis
r = await _get_redis()
doc_ids = await r.smembers(f"rag:idx:{collection or 'known_scams'}")

View file

@ -8,9 +8,9 @@ VPS_APP="/root/backend/app"
FILES=(
# Core RAG (single source of truth)
"app/crypto_embeddings.py"
"app/domains/intelligence/crypto_embeddings.py"
"app/ann_index.py"
"app/rag_service.py"
"app/domains/intelligence/rag_service.py"
# Ingestion
"app/rag_chunking.py"
"app/rag_historical.py"

View file

@ -119,7 +119,7 @@ async def mine_hard_negatives(
# Try RAG search for false positives
try:
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
results = await three_pillar_search(
query=query,

View file

@ -91,7 +91,7 @@ async def ingest_via_api(collection: str, content: str, metadata: dict, doc_id:
logger.debug(f"API unavailable ({e}), falling back to direct import")
# Direct import fallback
from app.rag_service import ingest_document
from app.domains.intelligence.rag_service import ingest_document
return await ingest_document(collection, content, metadata, doc_id)

View file

@ -154,7 +154,7 @@ async def ingest_via_api(hacks, dry_run=False):
# Method 2: Direct Python import (run inside Docker)
# ─────────────────────────────────────────────────────────────
async def ingest_direct(hacks, dry_run=False):
from app.rag_service import ingest_document
from app.domains.intelligence.rag_service import ingest_document
success = 0
failed = 0

View file

@ -14,7 +14,7 @@ os.chdir("/root/backend")
import redis.asyncio as redis # noqa: E402
from app.crypto_embeddings import get_embedder # noqa: E402
from app.domains.intelligence.crypto_embeddings import get_embedder # noqa: E402
CSV_PATH = "/root/tools/dark-collection/sigmod-pnd/Data/Telegram/Labeled/pred_pump_message.csv"
REDIS_HOST = os.getenv("REDIS_HOST", "rmi-redis")

View file

@ -82,7 +82,7 @@ async def run_tests():
@test("extract_contract_features: returns 128-dim vector")
def test_contract_features_dims():
from app.crypto_embeddings import extract_contract_features
from app.domains.intelligence.crypto_embeddings import extract_contract_features
vec = extract_contract_features("pragma solidity; function mint() external onlyOwner {}")
assert len(vec) == 128, f"Expected 128 dims, got {len(vec)}"
@ -91,7 +91,7 @@ def test_contract_features_dims():
@test("extract_contract_features: detects rug patterns")
def test_contract_features_rug_detection():
from app.crypto_embeddings import extract_contract_features
from app.domains.intelligence.crypto_embeddings import extract_contract_features
rug_code = "function mint(address to, uint256 amount) external onlyOwner { maxTxAmount = 0; }"
vec = extract_contract_features(rug_code)
@ -106,7 +106,7 @@ def test_contract_features_rug_detection():
@test("extract_contract_features: empty input returns zeros")
def test_contract_features_empty():
from app.crypto_embeddings import extract_contract_features
from app.domains.intelligence.crypto_embeddings import extract_contract_features
vec = extract_contract_features("")
assert len(vec) == 128
@ -115,7 +115,7 @@ def test_contract_features_empty():
@test("extract_transaction_features: returns 64-dim vector")
def test_transaction_features_dims():
from app.crypto_embeddings import extract_transaction_features
from app.domains.intelligence.crypto_embeddings import extract_transaction_features
vec = extract_transaction_features({"transactions": []})
assert len(vec) == 64, f"Expected 64 dims, got {len(vec)}"
@ -123,7 +123,7 @@ def test_transaction_features_dims():
@test("extract_transaction_features: captures counterparty diversity")
def test_transaction_features_counterparty():
from app.crypto_embeddings import extract_transaction_features
from app.domains.intelligence.crypto_embeddings import extract_transaction_features
txs = [
{"amount": 100, "from": "0xA", "to": "0xB", "timestamp": 1000},
@ -136,7 +136,7 @@ def test_transaction_features_counterparty():
@test("extract_wallet_features: returns 64-dim vector")
def test_wallet_features_dims():
from app.crypto_embeddings import extract_wallet_features
from app.domains.intelligence.crypto_embeddings import extract_wallet_features
vec = extract_wallet_features({"labels": ["scammer"], "balance_usd": 5000})
assert len(vec) == 64, f"Expected 64 dims, got {len(vec)}"
@ -144,7 +144,7 @@ def test_wallet_features_dims():
@test("extract_wallet_features: detects risk labels")
def test_wallet_features_labels():
from app.crypto_embeddings import extract_wallet_features
from app.domains.intelligence.crypto_embeddings import extract_wallet_features
vec = extract_wallet_features({"labels": ["scammer", "rug_puller"]})
# dim 20 = scammer, dim 21 = rug_puller
@ -159,7 +159,7 @@ def test_wallet_features_labels():
@test("cosine_similarity: identical vectors = 1.0")
def test_cosine_same():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
v = [1.0, 0.0, 0.5, 0.3]
sim = CryptoEmbedder.cosine_similarity(v, v)
@ -168,7 +168,7 @@ def test_cosine_same():
@test("cosine_similarity: orthogonal vectors = 0.0")
def test_cosine_orthogonal():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
a = [1.0, 0.0, 0.0]
b = [0.0, 1.0, 0.0]
@ -178,7 +178,7 @@ def test_cosine_orthogonal():
@test("cosine_similarity: opposite vectors = -1.0")
def test_cosine_opposite():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
a = [1.0, 0.0]
b = [-1.0, 0.0]
@ -188,7 +188,7 @@ def test_cosine_opposite():
@test("cosine_similarity: zero vector = 0.0")
def test_cosine_zero():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
sim = CryptoEmbedder.cosine_similarity([0, 0, 0], [1, 0, 0])
assert sim == 0.0, f"Zero vector sim={sim}"
@ -201,7 +201,7 @@ def test_cosine_zero():
@test("hash_embed: returns 384-dim normalized vector")
def test_hash_embed():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
embedder = CryptoEmbedder()
vec = embedder._hash_embed("test wallet scam pattern")
@ -215,7 +215,7 @@ def test_hash_embed():
@test("hash_embed: deterministic (same input = same output)")
def test_hash_embed_deterministic():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
embedder = CryptoEmbedder()
v1 = embedder._hash_embed("rug pull honeypot")
@ -225,7 +225,7 @@ def test_hash_embed_deterministic():
@test("hash_embed: different inputs = different outputs")
def test_hash_embed_unique():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
embedder = CryptoEmbedder()
v1 = embedder._hash_embed("rug pull")
@ -507,7 +507,7 @@ def test_parent_child():
@test("embedding cache: _cache_key generates deterministic keys")
async def test_cache_key():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
embedder = CryptoEmbedder()
key1 = await embedder._cache_key("semantic", "test text")
@ -517,7 +517,7 @@ async def test_cache_key():
@test("embedding cache: different heads generate different keys")
async def test_cache_key_different_heads():
from app.crypto_embeddings import CryptoEmbedder
from app.domains.intelligence.crypto_embeddings import CryptoEmbedder
embedder = CryptoEmbedder()
key1 = await embedder._cache_key("semantic", "test")
@ -567,7 +567,7 @@ def test_get_dim_fallback():
@test("KNOWN_SCAM_PATTERNS: has required fields")
def test_scam_patterns_structure():
from app.crypto_embeddings import KNOWN_SCAM_PATTERNS
from app.domains.intelligence.crypto_embeddings import KNOWN_SCAM_PATTERNS
assert len(KNOWN_SCAM_PATTERNS) >= 10, (
f"Expected >= 10 patterns, got {len(KNOWN_SCAM_PATTERNS)}"
@ -605,7 +605,7 @@ def test_ttl_permanent():
# We verify by inspecting the source - TTL=0 means no expiry
import inspect
from app.rag_service import ingest_document
from app.domains.intelligence.rag_service import ingest_document
source = inspect.getsource(ingest_document)
assert "forensic_reports" in source, "forensic_reports missing from TTL logic"
@ -637,7 +637,7 @@ def test_model_config():
@test("COLLECTIONS list includes all expected collections")
def test_collections():
from app.crypto_embeddings import COLLECTIONS
from app.domains.intelligence.crypto_embeddings import COLLECTIONS
expected = [
"wallet_profiles",
@ -672,7 +672,7 @@ async def test_ann_build():
@test("ANNIndex: search returns hydrated results with content")
async def test_ann_search():
from app.ann_index import ANNIndex
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
idx = ANNIndex()
await idx.build_index("scam_patterns")
@ -691,7 +691,7 @@ async def test_ann_speed():
import time
from app.ann_index import ANNIndex
from app.crypto_embeddings import get_embedder
from app.domains.intelligence.crypto_embeddings import get_embedder
idx = ANNIndex()
await idx.build_index("scam_patterns")
@ -761,7 +761,7 @@ async def test_semantic_cache_stats():
@test("three_pillar_search: returns results with pillar attribution")
async def test_three_pillar():
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
result = await three_pillar_search(
"honeypot token with high sell tax",
@ -778,7 +778,7 @@ async def test_three_pillar():
@test("three_pillar_search: entity extraction from query")
async def test_three_pillar_entity():
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
result = await three_pillar_search(
"token on solana and ethereum",
@ -793,7 +793,7 @@ async def test_three_pillar_entity():
@test("three_pillar_search: RRF fusion produces ranked results")
async def test_three_pillar_rrf():
from app.rag_service import three_pillar_search
from app.domains.intelligence.rag_service import three_pillar_search
result = await three_pillar_search(
"rug pull honeypot",
@ -911,8 +911,8 @@ def test_ragas_mrr():
@test("scam pattern cache: pre_embed caches all 10 patterns")
async def test_pattern_cache():
from app.crypto_embeddings import KNOWN_SCAM_PATTERNS
from app.rag_service import _pattern_cache, _preembed_scam_patterns
from app.domains.intelligence.crypto_embeddings import KNOWN_SCAM_PATTERNS
from app.domains.intelligence.rag_service import _pattern_cache, _preembed_scam_patterns
await _preembed_scam_patterns()
assert len(_pattern_cache) == len(KNOWN_SCAM_PATTERNS), (
@ -922,7 +922,7 @@ async def test_pattern_cache():
@test("scam pattern cache: cached patterns have embedding vectors")
async def test_pattern_cache_vectors():
from app.rag_service import _pattern_cache, _preembed_scam_patterns
from app.domains.intelligence.rag_service import _pattern_cache, _preembed_scam_patterns
await _preembed_scam_patterns()
for name, result in _pattern_cache.items():

View file

@ -6,7 +6,7 @@ from unittest.mock import AsyncMock, patch
import pytest
from app.rug_imminence_predictor import (
from app.domains.scanners.rug_imminence_predictor import (
DEFAULT_WEIGHTS,
ImminenceLevel,
RIPResult,
@ -281,7 +281,7 @@ class TestRugImminencePredictor:
name=sig_id.replace("_", " ").title(),
weight=weight,
)
with patch("app.scanners.social_velocity.SocialVelocityAnalyzer") as mock:
with patch("app.domains.scanners.social_velocity.SocialVelocityAnalyzer") as mock:
mock_instance = AsyncMock()
mock_instance.analyze = AsyncMock(
return_value={
@ -305,7 +305,7 @@ class TestRugImminencePredictor:
name=sig_id.replace("_", " ").title(),
weight=weight,
)
with patch("app.scanners.honeypot_detector.HoneypotDetector") as mock_hp:
with patch("app.domains.scanners.honeypot_detector.HoneypotDetector") as mock_hp:
mock_instance = AsyncMock()
mock_instance.detect = AsyncMock(
return_value={
@ -314,7 +314,7 @@ class TestRugImminencePredictor:
}
)
mock_hp.return_value = mock_instance
with patch("app.scanners.contract_authority.ContractAuthorityScanner") as mock_auth:
with patch("app.domains.scanners.contract_authority.ContractAuthorityScanner") as mock_auth:
auth_instance = AsyncMock()
auth_instance.scan = AsyncMock(
return_value={

View file

@ -9,7 +9,7 @@ parsing - all without requiring network calls.
import unittest
from datetime import UTC, datetime
from app.flash_loan_attack_detector import (
from app.domains.scanners.flash_loan_attack_detector import (
AAVE_V2_FLASHLOAN_SIG,
BALANCER_FLASHLOAN_SIG,
FLASHLOAN_PROVIDERS,

View file

@ -16,7 +16,7 @@ import sys
# Add app path so we can import
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from app.liquidation_cascade_analyzer import (
from app.domains.scanners.liquidation_cascade_analyzer import (
CascadeScenario,
CollateralPosition,
DebtPosition,

View file

@ -9,7 +9,7 @@ network calls.
import unittest
from unittest.mock import AsyncMock, patch
from app.mev_sandwich_detector import (
from app.domains.scanners.mev_sandwich_detector import (
KNOWN_MEV_BOTS,
MEVBotProfile,
MEVSandwichDetector,

View file

@ -10,7 +10,7 @@ all without requiring network calls.
import json
import unittest
from app.oracle_manipulation_detector import (
from app.domains.scanners.oracle_manipulation_detector import (
CHAINLINK_FEEDS,
CROSS_POOL_DIVERGENCE_THRESHOLD,
KNOWN_LP_POOLS,
@ -422,7 +422,7 @@ class TestPriceManipulationReport(unittest.TestCase):
"""ManipulationReport aggregation tests."""
def test_risk_score_calculation(self):
from app.oracle_manipulation_detector import ManipulationReport
from app.domains.scanners.oracle_manipulation_detector import ManipulationReport
report = ManipulationReport(
scan_id="test",
@ -444,7 +444,7 @@ class TestPriceManipulationReport(unittest.TestCase):
self.assertEqual(report.risk_score, 1.0)
def test_incident_counts(self):
from app.oracle_manipulation_detector import ManipulationReport
from app.domains.scanners.oracle_manipulation_detector import ManipulationReport
report = ManipulationReport(
scan_id="test",
@ -483,7 +483,7 @@ class TestPriceManipulationReport(unittest.TestCase):
self.assertEqual(report.total_incidents, 3)
def test_duration(self):
from app.oracle_manipulation_detector import ManipulationReport
from app.domains.scanners.oracle_manipulation_detector import ManipulationReport
report = ManipulationReport(
scan_id="test",

View file

@ -9,7 +9,7 @@ from pathlib import Path
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from app.portfolio_risk_aggregator import (
from app.domains.scanners.portfolio_risk_aggregator import (
ChainPortfolio,
PortfolioRiskAggregator,
PortfolioRiskProfile,
@ -236,7 +236,7 @@ def test_token_risk_scorer_heuristic():
def test_validate_wallet_address():
"""Test wallet address validation."""
from app.portfolio_risk_aggregator import validate_wallet_address
from app.domains.scanners.portfolio_risk_aggregator import validate_wallet_address
# Valid EVM
valid, hint = validate_wallet_address("0x742d35Cc6634C0532925a3b844Bc454e4438f44e")
@ -269,7 +269,7 @@ def test_validate_wallet_address():
async def test_price_oracle_defaults():
"""Test PriceOracle has sensible defaults."""
from app.portfolio_risk_aggregator import PriceOracle
from app.domains.scanners.portfolio_risk_aggregator import PriceOracle
oracle = PriceOracle()
assert await oracle.get_price("ETH") == 2800.0

View file

@ -8,7 +8,7 @@ from pathlib import Path
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from app.pump_dump_manipulation_detector import (
from app.domains.scanners.pump_dump_manipulation_detector import (
PUMP_DUMP_THRESHOLDS,
CoordinatedBuyGroup,
FindingSeverity,

View file

@ -9,7 +9,7 @@ from pathlib import Path
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from app.smart_contract_honeypot_detector import (
from app.domains.scanners.smart_contract_honeypot_detector import (
HONEYPOT_THRESHOLDS,
FindingSeverity,
HoneypotAnalysisResult,
@ -306,7 +306,7 @@ def test_risk_score_thresholds() -> None:
def test_known_malicious_selectors() -> None:
"""Test that known malicious selectors are properly defined."""
from app.smart_contract_honeypot_detector import KNOWN_MALICIOUS_SELECTORS
from app.domains.scanners.smart_contract_honeypot_detector import KNOWN_MALICIOUS_SELECTORS
assert "0x40c10f19" in KNOWN_MALICIOUS_SELECTORS # mint(address,uint256)
assert "0x24b6d0c4" in KNOWN_MALICIOUS_SELECTORS # blacklist
@ -326,7 +326,7 @@ def test_known_malicious_selectors() -> None:
def test_benign_selectors() -> None:
"""Test benign selectors don't conflict with malicious ones."""
from app.smart_contract_honeypot_detector import (
from app.domains.scanners.smart_contract_honeypot_detector import (
BENIGN_SELECTORS,
KNOWN_MALICIOUS_SELECTORS,
)
@ -348,7 +348,7 @@ def test_honeypot_thresholds() -> None:
def test_evm_chains() -> None:
"""Test EVM chain configuration is complete."""
from app.smart_contract_honeypot_detector import EVM_CHAINS
from app.domains.scanners.smart_contract_honeypot_detector import EVM_CHAINS
required_chains = {"ethereum", "bsc", "polygon", "arbitrum", "base"}
for chain in required_chains: