Files
aitbc/website/agent/live_api.py
aitbc a1a3711092
All checks were successful
Cross-Node Transaction Testing / transaction-test (push) Successful in 3s
Deploy to Testnet / deploy-testnet (push) Successful in 1m19s
Multi-Node Stress Testing / stress-test (push) Successful in 3s
refactor: remove static agent discovery JSON files - migrate to live RPC endpoints
- Deleted chains.json, discovery.json, islands.json static configuration files
- Removed /agent/join/ait-mainnet.json configuration guide
- Updated index.html to show only mainnet island, removed testnet card
- Changed note from "Agent-First Design" to "Live Data Only" emphasizing real-time RPC queries
- Added "Live data from RPC" indicator to mainnet island card
- All agent endpoints now serve dynamic data from blockchain
2026-05-19 19:03:30 +02:00

318 lines
11 KiB
Python

#!/usr/bin/env python3
"""
AITBC Agent Live API
Real-time dynamic endpoints that query blockchain RPC and serve fresh data
"""
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, Any, Optional
# Try to import fastapi, fallback to flask
try:
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
import uvicorn
HAS_FASTAPI = True
except ImportError:
try:
from flask import Flask, jsonify, abort
HAS_FLASK = True
except ImportError:
print("Error: Neither FastAPI nor Flask installed")
sys.exit(1)
import urllib.request
import urllib.error
import ssl
# Configuration
RPC_URL = os.getenv("AITBC_RPC_URL", "http://localhost:8006/rpc")
NODE_ID = os.getenv("NODE_ID", "aitbc")
ISLAND_ID = os.getenv("ISLAND_ID", "ait-mainnet-island")
CHAIN_ID = os.getenv("CHAIN_ID", "ait-mainnet")
NODE_ROLE = os.getenv("NODE_ROLE", "hub")
# SSL context that doesn't verify certs (for local dev)
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
def rpc_query(method: str, params: Optional[list] = None) -> Optional[Dict]:
"""Query the blockchain RPC endpoint"""
try:
url = f"{RPC_URL}/{method}"
req = urllib.request.Request(
url,
headers={
"Accept": "application/json",
"Content-Type": "application/json"
}
)
with urllib.request.urlopen(req, timeout=5, context=ssl_context) as response:
return json.loads(response.read().decode())
except Exception as e:
print(f"RPC query failed: {e}", file=sys.stderr)
return None
def get_live_island_data() -> Dict[str, Any]:
"""Get real-time island data from RPC"""
# Query chain head for current block info
head = rpc_query("head")
info = rpc_query("info")
islands_rpc = rpc_query("islands")
# Get peer count from island manager if available
peer_count = 0
if islands_rpc and isinstance(islands_rpc, dict) and "islands" in islands_rpc:
for island in islands_rpc["islands"]:
if island.get("island_id") == ISLAND_ID:
peer_count = island.get("peer_count", 0)
break
block_height = head.get("height", 0) if head else 0
block_hash = head.get("hash", "unknown") if head else "unknown"
timestamp = head.get("timestamp", datetime.now(timezone.utc).isoformat()) if head else datetime.now(timezone.utc).isoformat()
return {
"islands": [
{
"island_id": ISLAND_ID,
"island_name": "AIT Mainnet" if CHAIN_ID == "ait-mainnet" else "AIT Testnet",
"chain_id": CHAIN_ID,
"status": "active",
"role": NODE_ROLE,
"chain_info": {
"block_time": 5,
"consensus": "proof_of_authority",
"network_id": 1337,
"current_height": block_height,
"current_hash": block_hash,
"last_update": timestamp
},
"endpoints": {
"rpc": [
{"url": f"http://{NODE_ID}:8006", "node_id": NODE_ID, "role": NODE_ROLE},
{"url": f"http://{'aitbc1' if NODE_ID == 'aitbc' else 'aitbc'}:8006",
"node_id": 'aitbc1' if NODE_ID == 'aitbc' else 'aitbc',
"role": "follower" if NODE_ROLE == "hub" else "hub"}
],
"p2p": [
{"address": f"{NODE_ID}:7070", "node_id": NODE_ID},
{"address": f"{'aitbc1' if NODE_ID == 'aitbc' else 'aitbc'}:7070",
"node_id": 'aitbc1' if NODE_ID == 'aitbc' else 'aitbc'}
]
},
"stats": {
"peer_count": peer_count,
"block_height": block_height,
"last_block_hash": block_hash,
"last_block_time": timestamp
},
"this_node": {
"node_id": NODE_ID,
"role": NODE_ROLE,
"is_hub": NODE_ROLE == "hub",
"block_production_chains": [CHAIN_ID] if NODE_ROLE == "hub" else [],
"enable_block_production": NODE_ROLE == "hub"
}
}
],
"_meta": {
"generated_at": datetime.now(timezone.utc).isoformat(),
"format_version": "1.0",
"total_islands": 1,
"active_islands": 1,
"data_source": "live_rpc",
"note": "This endpoint shows real-time data from the blockchain RPC"
}
}
def get_live_chain_data() -> Dict[str, Any]:
"""Get real-time chain data from RPC"""
head = rpc_query("head")
info = rpc_query("info")
block_height = head.get("height", 0) if head else 0
block_hash = head.get("hash", "unknown") if head else "unknown"
timestamp = head.get("timestamp", datetime.now(timezone.utc).isoformat()) if head else datetime.now(timezone.utc).isoformat()
tx_count = head.get("tx_count", 0) if head else 0
return {
"chains": [
{
"chain_id": CHAIN_ID,
"name": "AIT Mainnet" if CHAIN_ID == "ait-mainnet" else "AIT Testnet",
"island_id": ISLAND_ID,
"type": "production" if CHAIN_ID == "ait-mainnet" else "test",
"status": "active",
"live_stats": {
"current_height": block_height,
"current_hash": block_hash,
"last_block_time": timestamp,
"tx_count_last_block": tx_count,
"queried_at": datetime.now(timezone.utc).isoformat()
},
"config": {
"block_time": info.get("block_time", 5) if info else 5,
"consensus": "proof_of_authority",
"network_id": info.get("network_id", 1337) if info else 1337
},
"endpoints": {
"rpc": [
f"http://{NODE_ID}:8006/rpc",
f"http://{'aitbc1' if NODE_ID == 'aitbc' else 'aitbc'}:8006/rpc"
],
"head": "/rpc/head",
"info": "/rpc/info",
"supply": "/rpc/supply"
},
"join": {
"guide": f"/agent/join/{CHAIN_ID}.json"
}
}
],
"_meta": {
"generated_at": datetime.now(timezone.utc).isoformat(),
"format_version": "1.0",
"total_chains": 1,
"active_chains": 1,
"data_source": "live_rpc"
}
}
def get_live_discovery() -> Dict[str, Any]:
"""Get live discovery data"""
head = rpc_query("head")
health = rpc_query("health") or {}
block_height = head.get("height", 0) if head else 0
return {
"network": {
"name": "AITBC",
"version": "1.0.0",
"description": "AI-powered blockchain platform",
"live_status": {
"current_height": block_height,
"node_health": health.get("status", "unknown"),
"last_update": datetime.now(timezone.utc).isoformat()
},
"apis": {
"rpc": {
"url": f"http://{NODE_ID}:8006/rpc",
"documentation": "/agent/openapi.json"
},
"agent": {
"discovery": "/agent/discovery.json",
"islands": "/agent/islands.json",
"chains": "/agent/chains.json",
"health": "/agent/health"
}
},
"islands": [
{
"island_id": ISLAND_ID,
"name": "AIT Mainnet" if CHAIN_ID == "ait-mainnet" else "AIT Testnet",
"chain_id": CHAIN_ID,
"current_height": block_height,
"status": "active",
"join_guide": f"/agent/join/{CHAIN_ID}.json",
"note": f"This node ({NODE_ID}) is the HUB for this island"
}
]
},
"this_node": {
"node_id": NODE_ID,
"role": NODE_ROLE,
"is_hub": NODE_ROLE == "hub",
"block_production": NODE_ROLE == "hub",
"chains": [CHAIN_ID] if NODE_ROLE == "hub" else [],
"island_memberships": [ISLAND_ID],
"live_data": True
},
"_meta": {
"generated_at": datetime.now(timezone.utc).isoformat(),
"format_version": "1.1",
"data_source": "live_rpc"
}
}
# Create app based on available framework
if HAS_FASTAPI:
app = FastAPI(title="AITBC Agent Live API")
@app.get("/agent/islands.json")
async def live_islands():
return JSONResponse(content=get_live_island_data())
@app.get("/agent/chains.json")
async def live_chains():
return JSONResponse(content=get_live_chain_data())
@app.get("/agent/discovery.json")
async def live_discovery():
return JSONResponse(content=get_live_discovery())
@app.get("/agent/health")
async def live_health():
head = rpc_query("head")
return JSONResponse(content={
"status": "healthy" if head else "unhealthy",
"node_id": NODE_ID,
"chain_id": CHAIN_ID,
"current_height": head.get("height", 0) if head else 0,
"timestamp": datetime.now(timezone.utc).isoformat()
})
elif HAS_FLASK:
app = Flask(__name__)
@app.route("/agent/islands.json")
def live_islands():
return jsonify(get_live_island_data())
@app.route("/agent/chains.json")
def live_chains():
return jsonify(get_live_chain_data())
@app.route("/agent/discovery.json")
def live_discovery():
return jsonify(get_live_discovery())
@app.route("/agent/health")
def live_health():
head = rpc_query("head")
return jsonify({
"status": "healthy" if head else "unhealthy",
"node_id": NODE_ID,
"chain_id": CHAIN_ID,
"current_height": head.get("height", 0) if head else 0,
"timestamp": datetime.now(timezone.utc).isoformat()
})
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=8080)
parser.add_argument("--host", default="127.0.0.1")
args = parser.parse_args()
print(f"Starting AITBC Agent Live API on {args.host}:{args.port}")
print(f"RPC URL: {RPC_URL}")
print(f"Node: {NODE_ID} | Chain: {CHAIN_ID} | Role: {NODE_ROLE}")
if HAS_FASTAPI:
uvicorn.run(app, host=args.host, port=args.port)
elif HAS_FLASK:
app.run(host=args.host, port=args.port)