- Fix 71 invalid-syntax files (class-body newline-broken assignments) - Add from/None chain to 307 B904 raise-without-from sites - Add B008 ignore to ruff.toml (already in pyproject.toml) - Noqa F401 on __init__.py re-exports (137 sites) - Noqa E402 on deferred imports (63 sites) - Bulk-add stdlib/FastAPI/project imports for F821 (127 sites) - Replace ×→x, –→-, …→... in docstrings (4093 chars) - Manual refactor of 5 SIM103/SIM116 patterns Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py) Co-authored-by: opencode <opencode@rugmunch.io>
286 lines
9.1 KiB
Python
286 lines
9.1 KiB
Python
"""
|
|
RMI Tool Changelog & Version System
|
|
=====================================
|
|
Tracks tool versions, changes, and deprecations.
|
|
Provides transparency for tool quality and evolution.
|
|
|
|
Endpoints:
|
|
GET /api/v1/tools/changelog - Full changelog
|
|
GET /api/v1/tools/changelog/{tool} - Changelog for specific tool
|
|
GET /api/v1/tools/version/{tool} - Current version for tool
|
|
GET /api/v1/tools/deprecated - List of deprecated tools
|
|
|
|
Author: RMI Development
|
|
Date: 2026-06-05
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from datetime import UTC, datetime
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from app.core.redis import get_redis
|
|
|
|
logger = logging.getLogger("tool_changelog")
|
|
|
|
router = APIRouter(prefix="/api/v1/tools", tags=["tool-changelog"])
|
|
|
|
|
|
# ── Redis Helper ─────────────────────────────────────────────────
|
|
|
|
|
|
INITIAL_CHANGELOG = [
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "feature",
|
|
"tool": "whale_copy_trade",
|
|
"title": "New Tool: Whale Copy Trade Engine",
|
|
"description": "Real-time copy trade engine - input a smart money wallet, get their exact last 24h trades with entry/exit prices, PnL, and suggested follow trades.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "feature",
|
|
"tool": "rug_predictor_live",
|
|
"title": "New Tool: Live Rug Predictor",
|
|
"description": "Analyzes tokens in their first 5-30 minutes with 6-signal rug probability scoring. Detects low liquidity, unlocked LP, price crashes, mass selling, and more.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "feature",
|
|
"tool": "whale_cluster",
|
|
"title": "New Tool: Whale Cluster Detection",
|
|
"description": "Identify coordinated whale clusters - wallets that move together via same funding source, timing patterns, and token sets. Detects wash trading and insider networks.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "infrastructure",
|
|
"tool": "*",
|
|
"title": "Developer Tier System",
|
|
"description": "Free developer API keys with 100 calls/day, no payment required. Tier system: FREE (100/day), TRIAL (3-5/tool), PAID (pay-per-use), PRO ($29/mo, 5000/day).",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "infrastructure",
|
|
"tool": "*",
|
|
"title": "Facilitator Health Monitoring",
|
|
"description": "60s health check loop for all payment facilitators with latency tracking, success rate monitoring, and auto-failover readiness.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "infrastructure",
|
|
"tool": "*",
|
|
"title": "Public Status Page",
|
|
"description": "Public-facing status page at /api/v1/status showing health of all RMI services - backend, Redis, ClickHouse, MCP, facilitators. 30s monitoring loop.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "infrastructure",
|
|
"tool": "*",
|
|
"title": "Webhook Notification Pipeline",
|
|
"description": "Real-time webhook delivery system with retry, signing, rate limiting, and dead letter queue. Polls every 5 seconds for new alerts.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "infrastructure",
|
|
"tool": "*",
|
|
"title": "Persistent State Layer",
|
|
"description": "User watchlists, portfolios, saved scans, and alert history. Redis-backed with JSON serialization.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-05",
|
|
"version": "3.3.0",
|
|
"type": "improvement",
|
|
"tool": "*",
|
|
"title": "SDK v2.0 Released",
|
|
"description": "Complete rewrite with async/sync dual API, retry with exponential backoff, batch tool calls, webhook management, proper error classes, and type hints.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-01",
|
|
"version": "3.2.0",
|
|
"type": "improvement",
|
|
"tool": "*",
|
|
"title": "Response Enrichment Pipeline",
|
|
"description": "Every tool response now enriched with wallet labels, RAG similarity search, scam pattern detection, risk summary, intel briefing, and confidence scores.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-01",
|
|
"version": "3.2.0",
|
|
"type": "feature",
|
|
"tool": "composite_score",
|
|
"title": "New Tool: Composite Score",
|
|
"description": "One-number buy/sell/avoid score combining reputation, rug probability, market health, narrative sentiment, MEV exposure, and DeFi position.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-06-01",
|
|
"version": "3.2.0",
|
|
"type": "feature",
|
|
"tool": "smart_money",
|
|
"title": "New Tool: Smart Money Finder",
|
|
"description": "Find profitable traders via win rate estimation, token count, balance analysis, and wallet label cross-reference.",
|
|
"breaking": False,
|
|
},
|
|
{
|
|
"date": "2026-05-28",
|
|
"version": "3.1.0",
|
|
"type": "infrastructure",
|
|
"tool": "*",
|
|
"title": "Multi-Facilitator Payment System",
|
|
"description": "Smart router auto-picks best facilitator per chain/token. 7 active facilitators across 11 payment chains.",
|
|
"breaking": False,
|
|
},
|
|
]
|
|
|
|
|
|
# ── Changelog Management ─────────────────────────────────────────
|
|
|
|
|
|
def get_changelog(tool: str | None = None, limit: int = 50) -> list[dict[str, Any]]:
|
|
"""Get changelog entries."""
|
|
r = get_redis()
|
|
|
|
# Load from Redis if available
|
|
entries = []
|
|
if r:
|
|
stored = r.lrange("rmi:changelog", 0, -1)
|
|
entries = [json.loads(e) for e in stored]
|
|
|
|
# If no stored entries, use initial data
|
|
if not entries:
|
|
entries = INITIAL_CHANGELOG.copy()
|
|
if r:
|
|
# Store initial entries
|
|
for entry in reversed(entries):
|
|
r.lpush("rmi:changelog", json.dumps(entry))
|
|
|
|
# Filter by tool
|
|
if tool:
|
|
entries = [e for e in entries if e.get("tool") == tool or e.get("tool") == "*"]
|
|
|
|
# Limit
|
|
return entries[:limit]
|
|
|
|
|
|
def add_changelog_entry(
|
|
tool: str,
|
|
title: str,
|
|
description: str,
|
|
version: str = "",
|
|
change_type: str = "improvement",
|
|
breaking: bool = False,
|
|
):
|
|
"""Add a changelog entry."""
|
|
r = get_redis()
|
|
if not r:
|
|
return
|
|
|
|
entry = {
|
|
"date": datetime.now(UTC).strftime("%Y-%m-%d"),
|
|
"version": version or "current",
|
|
"type": change_type,
|
|
"tool": tool,
|
|
"title": title,
|
|
"description": description,
|
|
"breaking": breaking,
|
|
}
|
|
|
|
r.lpush("rmi:changelog", json.dumps(entry))
|
|
r.ltrim("rmi:changelog", 0, 499) # Keep last 500
|
|
|
|
|
|
def get_tool_version(tool: str) -> dict[str, Any]:
|
|
"""Get current version info for a tool."""
|
|
changelog = get_changelog(tool, limit=1)
|
|
if changelog:
|
|
entry = changelog[0]
|
|
return {
|
|
"tool": tool,
|
|
"version": entry.get("version", "unknown"),
|
|
"last_updated": entry.get("date", ""),
|
|
"last_change": entry.get("title", ""),
|
|
}
|
|
|
|
# Default version
|
|
return {
|
|
"tool": tool,
|
|
"version": "1.0.0",
|
|
"last_updated": "unknown",
|
|
"last_change": "No changelog entries",
|
|
}
|
|
|
|
|
|
def get_deprecated_tools() -> list[dict[str, Any]]:
|
|
"""Get list of deprecated tools."""
|
|
r = get_redis()
|
|
if not r:
|
|
return []
|
|
|
|
deprecated = r.smembers("rmi:tools:deprecated")
|
|
return [json.loads(d) for d in deprecated]
|
|
|
|
|
|
# ── Endpoints ────────────────────────────────────────────────────
|
|
|
|
|
|
@router.get("/changelog")
|
|
async def tool_changelog(tool: str | None = None, limit: int = 50):
|
|
"""Get changelog entries."""
|
|
entries = get_changelog(tool, limit)
|
|
return JSONResponse(
|
|
content={
|
|
"changelog": entries,
|
|
"total": len(entries),
|
|
"filter": tool,
|
|
}
|
|
)
|
|
|
|
|
|
@router.get("/changelog/{tool}")
|
|
async def tool_specific_changelog(tool: str, limit: int = 20):
|
|
"""Get changelog for a specific tool."""
|
|
entries = get_changelog(tool, limit)
|
|
return JSONResponse(
|
|
content={
|
|
"tool": tool,
|
|
"changelog": entries,
|
|
"total": len(entries),
|
|
}
|
|
)
|
|
|
|
|
|
@router.get("/version/{tool}")
|
|
async def tool_version(tool: str):
|
|
"""Get current version for a tool."""
|
|
return JSONResponse(content=get_tool_version(tool))
|
|
|
|
|
|
@router.get("/deprecated")
|
|
async def deprecated_tools():
|
|
"""Get list of deprecated tools."""
|
|
return JSONResponse(
|
|
content={
|
|
"deprecated": get_deprecated_tools(),
|
|
"count": len(get_deprecated_tools()),
|
|
}
|
|
)
|