110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
IX Scout — Global Hardware Node Registry
|
||
Part of the Inference-X Ecosystem
|
||
# SALKA ELMADANI | inference-x.com | BSL-1.1
|
||
Copyright (C) 2024-2026 Salka Elmadani. BSL-1.1.
|
||
https://git.inference-x.com/inference-x-community/ix-scout
|
||
|
||
Real-time map of every IX node on Earth.
|
||
Anonymous. Voluntary. Community-powered.
|
||
"""
|
||
from fastapi import FastAPI, Request
|
||
from fastapi.responses import JSONResponse, HTMLResponse
|
||
from fastapi.middleware.cors import CORSMiddleware
|
||
import sqlite3, json, time, os, hashlib
|
||
|
||
app = FastAPI(title="IX Scout", version="1.0.0",
|
||
description="Real-time global map of every Inference-X node.")
|
||
app.add_middleware(CORSMiddleware, allow_origins=["*"],
|
||
allow_methods=["*"], allow_headers=["*"])
|
||
|
||
DB = os.environ.get("SCOUT_DB", "./scout.db")
|
||
|
||
def db():
|
||
conn = sqlite3.connect(DB)
|
||
conn.row_factory = sqlite3.Row
|
||
return conn
|
||
|
||
def init_db():
|
||
with db() as c:
|
||
c.execute("""CREATE TABLE IF NOT EXISTS nodes (
|
||
id TEXT PRIMARY KEY,
|
||
backend TEXT NOT NULL,
|
||
model TEXT,
|
||
tokens_per_sec REAL DEFAULT 0,
|
||
ram_gb REAL DEFAULT 0,
|
||
country TEXT DEFAULT '',
|
||
version TEXT DEFAULT '1.0',
|
||
reported_at INTEGER,
|
||
load_pct REAL DEFAULT 0
|
||
)""")
|
||
c.commit()
|
||
|
||
@app.on_event("startup")
|
||
async def startup(): init_db()
|
||
|
||
@app.post("/report")
|
||
async def report_node(request: Request):
|
||
"""IX Engine nodes report telemetry here (opt-in via --scout flag)."""
|
||
try:
|
||
data = await request.json()
|
||
node_id = hashlib.sha256(
|
||
f"{data.get('backend','')}{data.get('ram_gb',0)}".encode()
|
||
).hexdigest()[:16]
|
||
with db() as c:
|
||
c.execute("""INSERT OR REPLACE INTO nodes
|
||
(id,backend,model,tokens_per_sec,ram_gb,country,version,reported_at,load_pct)
|
||
VALUES (?,?,?,?,?,?,?,?,?)""",
|
||
(node_id, data.get("backend","cpu"), data.get("model",""),
|
||
float(data.get("tokens_per_sec",0)), float(data.get("ram_gb",0)),
|
||
data.get("country",""), data.get("version","1.0"),
|
||
int(time.time()), float(data.get("load_pct",0))))
|
||
c.commit()
|
||
return {"status":"ok","node_id":node_id}
|
||
except Exception as e:
|
||
return JSONResponse({"error":str(e)}, status_code=400)
|
||
|
||
@app.get("/stats")
|
||
async def stats():
|
||
"""Aggregated network stats — last 24 hours."""
|
||
cutoff = int(time.time()) - 86400
|
||
with db() as c:
|
||
backends = c.execute("""
|
||
SELECT backend, COUNT(*) as nodes,
|
||
AVG(tokens_per_sec) as avg_tps,
|
||
AVG(ram_gb) as avg_ram,
|
||
AVG(load_pct) as avg_load
|
||
FROM nodes WHERE reported_at > ?
|
||
GROUP BY backend ORDER BY nodes DESC
|
||
""", (cutoff,)).fetchall()
|
||
total = c.execute("SELECT COUNT(*) as t FROM nodes WHERE reported_at > ?",
|
||
(cutoff,)).fetchone()
|
||
return {
|
||
"total_nodes": total["t"] if total else 0,
|
||
"window": "24h",
|
||
"backends": [dict(b) for b in backends]
|
||
}
|
||
|
||
@app.get("/nodes")
|
||
async def list_nodes(limit: int = 100):
|
||
"""List recent nodes (anonymized)."""
|
||
cutoff = int(time.time()) - 3600
|
||
with db() as c:
|
||
rows = c.execute("""
|
||
SELECT backend, model, tokens_per_sec, ram_gb, country, version
|
||
FROM nodes WHERE reported_at > ? ORDER BY reported_at DESC LIMIT ?
|
||
""", (cutoff, limit)).fetchall()
|
||
return {"nodes": [dict(r) for r in rows], "count": len(rows)}
|
||
|
||
@app.get("/health")
|
||
async def health():
|
||
return {"status":"ok","service":"IX Scout","author":"Salka Elmadani","version":"1.0.0"}
|
||
|
||
if __name__ == "__main__":
|
||
import uvicorn
|
||
print("IX Scout — Global Hardware Node Registry")
|
||
print("Anonymous. Voluntary. Real.")
|
||
print("Built in Morocco for the world.")
|
||
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT","7936")))
|