refactor(domains): move 15 app-root monoliths into app/domains/ + lazy cloudscraper
This commit is contained in:
parent
25e0891a0d
commit
27184c704d
63 changed files with 145 additions and 136 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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:
|
||||
|
|
@ -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
|
||||
|
|
@ -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 []
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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'}")
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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={
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue