merge: chore/cleanup-remove-bloat-and-secrets into main
This commit is contained in:
commit
bde2f3a97d
1173 changed files with 437609 additions and 0 deletions
4
app/api/v1/catalog/__init__.py
Normal file
4
app/api/v1/catalog/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
"""Catalog v1 routes — thin HTTP layer."""
|
||||
from .router import router
|
||||
|
||||
__all__ = ["router"]
|
||||
155
app/api/v1/catalog/router.py
Normal file
155
app/api/v1/catalog/router.py
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
"""T27B HTTP routes — CatalogService endpoints.
|
||||
|
||||
Per v4.0 §T27. The thin HTTP layer over app.catalog.service.CatalogService.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.catalog.models import Chain
|
||||
from app.catalog.service import get_catalog
|
||||
|
||||
router = APIRouter(prefix="/api/v1/catalog", tags=["catalog"])
|
||||
|
||||
|
||||
# ── Request models ───────────────────────────────────────────────
|
||||
class RagIngestRequest(BaseModel):
|
||||
content: str = Field(..., min_length=1)
|
||||
collection: str = "scam_intel"
|
||||
doc_id: str | None = None
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class RagSearchRequest(BaseModel):
|
||||
query: str = Field(..., min_length=1)
|
||||
collection: str = "scam_intel"
|
||||
top_k: int = Field(default=5, ge=1, le=50)
|
||||
|
||||
|
||||
class ResolveEntityRequest(BaseModel):
|
||||
wallet_id: str
|
||||
max_chains: int = Field(default=5, ge=1, le=20)
|
||||
|
||||
|
||||
class FindRiskyTokensRequest(BaseModel):
|
||||
min_rug_count: int = Field(default=1, ge=1)
|
||||
chain: str | None = None
|
||||
limit: int = Field(default=50, ge=1, le=200)
|
||||
|
||||
|
||||
class AttachRagRequest(BaseModel):
|
||||
chain: str
|
||||
address: str
|
||||
qdrant_point_id: str
|
||||
|
||||
|
||||
# ── Health / introspection ───────────────────────────────────────
|
||||
@router.get("/stats")
|
||||
async def stats() -> dict:
|
||||
"""Catalog stats: which stores are reachable + entity counts."""
|
||||
return await get_catalog().stats()
|
||||
|
||||
|
||||
@router.get("/probe")
|
||||
async def probe() -> dict:
|
||||
"""Probe which stores are reachable from this container."""
|
||||
return await get_catalog().probe_stores()
|
||||
|
||||
|
||||
# ── Token endpoints ─────────────────────────────────────────────
|
||||
@router.get("/tokens/{chain}/{address}")
|
||||
async def get_token(chain: str, address: str) -> dict:
|
||||
"""Get a token by chain+address. Returns full Token model + provenance."""
|
||||
try:
|
||||
c = Chain(chain)
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"unknown chain: {chain}")
|
||||
tok = await get_catalog().get_token(c, address)
|
||||
if not tok:
|
||||
raise HTTPException(404, "token not found")
|
||||
return tok.model_dump(mode="json")
|
||||
|
||||
|
||||
@router.get("/tokens/{chain}/{address}/risk")
|
||||
async def get_token_risk(chain: str, address: str) -> dict:
|
||||
"""Recipe 3 — Real-time risk score. Composes Redis + Postgres + Neo4j."""
|
||||
try:
|
||||
c = Chain(chain)
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"unknown chain: {chain}")
|
||||
return await get_catalog().get_token_risk(c, address)
|
||||
|
||||
|
||||
@router.post("/tokens/risky-by-deployer")
|
||||
async def risky_tokens(req: FindRiskyTokensRequest) -> dict:
|
||||
"""Recipe 1 — Find tokens deployed by wallets with rug history."""
|
||||
chain_enum = None
|
||||
if req.chain:
|
||||
try:
|
||||
chain_enum = Chain(req.chain)
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"unknown chain: {req.chain}")
|
||||
tokens = await get_catalog().find_tokens_by_deployer_history(
|
||||
min_rug_count=req.min_rug_count, chain=chain_enum, limit=req.limit
|
||||
)
|
||||
return {
|
||||
"count": len(tokens),
|
||||
"tokens": [t.model_dump(mode="json") for t in tokens],
|
||||
}
|
||||
|
||||
|
||||
# ── Wallet endpoints ─────────────────────────────────────────────
|
||||
@router.get("/wallets/{chain}/{address}")
|
||||
async def get_wallet(chain: str, address: str) -> dict:
|
||||
try:
|
||||
c = Chain(chain)
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"unknown chain: {chain}")
|
||||
w = await get_catalog().get_wallet(c, address)
|
||||
if not w:
|
||||
raise HTTPException(404, "wallet not found")
|
||||
return w.model_dump(mode="json")
|
||||
|
||||
|
||||
# ── Entity resolution (Recipe 5) ────────────────────────────────
|
||||
@router.post("/entities/resolve")
|
||||
async def resolve_entity(req: ResolveEntityRequest) -> dict:
|
||||
"""Cross-chain entity resolution via Neo4j Cypher."""
|
||||
return await get_catalog().resolve_entity(req.wallet_id, req.max_chains)
|
||||
|
||||
|
||||
# ── RAG bridge endpoints ────────────────────────────────────────
|
||||
@router.post("/rag/search")
|
||||
async def rag_search(req: RagSearchRequest) -> dict:
|
||||
"""Search the RAG system. Returns ranked hits with RRF scores."""
|
||||
hits = await get_catalog().rag_search(
|
||||
query=req.query, collection=req.collection, top_k=req.top_k
|
||||
)
|
||||
return {"count": len(hits), "hits": hits}
|
||||
|
||||
|
||||
@router.post("/rag/ingest")
|
||||
async def rag_ingest(req: RagIngestRequest) -> dict:
|
||||
"""Ingest content into RAG. Returns qdrant_point_id for cross-store linking."""
|
||||
return await get_catalog().rag_ingest(
|
||||
content=req.content,
|
||||
collection=req.collection,
|
||||
doc_id=req.doc_id,
|
||||
metadata=req.metadata,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/tokens/{chain}/{address}/attach-rag")
|
||||
async def attach_rag(chain: str, address: str, req: AttachRagRequest) -> dict:
|
||||
"""Link an existing RAG embedding (Qdrant point) to a Token row."""
|
||||
try:
|
||||
c = Chain(chain)
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"unknown chain: {chain}")
|
||||
ok = await get_catalog().attach_rag_to_token(c, address, req.qdrant_point_id)
|
||||
if not ok:
|
||||
raise HTTPException(404, "token not found or update failed")
|
||||
return {"ok": True, "chain": chain, "address": address, "rag_embedding_id": req.qdrant_point_id}
|
||||
Loading…
Add table
Add a link
Reference in a new issue