Gensyn
git clone https://github.com/direkturcrypto/vikey-inference
cd vikey-inference
Edit fileΒ .env:
nano .env
Isi seperti ini:
VIKEY_API_KEY=ISI_DENGAN_API_KEY_KAMU
NODE_PORT=11434
Buat Screen
Screen -S vkey1
lalu jalankan
chmod +x vikey-inference-linux
./vikey-inference-linuxKalau muncul:
Ollama server running on port 14444
Buat Screen
Screen -S vkey1
Supaya inference tetap jalan meskipun kamu keluar dari terminal:
nohup ./vikey-inference-linux > vikey.log &
Artinya inference sudah jalan!, ketik ctrl + A +D untuk exit dari aplikasi ini
Edit file YAML konfigurasi:
nano hivemind_exp/configs/gpu/grpo-qwen-2.5-0.5b-deepseek-r1.yaml
Ubah:
use_vllm: true
Menjadi:
use_vllm: false
Ini akan menonaktifkan vLLM agar tidak jalanin model lokal.
Gensyn tidak tahu caranya pakai API eksternal secara default. Jadi kita:
β‘οΈ Tambahkan server proxy FastAPI yang akan menerima permintaan dari swarm, lalu teruskan ke Vikey AI.
Import Requests + Fungsi call ke Vikey (di paling atas):
nano web/api/server.py
import argparse
import json
import logging
import os
import time
from datetime import datetime, timedelta
from threading import Thread
import aiofiles
import httpx
import uvicorn
from fastapi import FastAPI, HTTPException, Query, Request, Response
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from pythonjsonlogger import jsonlogger
import requests # Added import for requests
from hivemind_exp.chain_utils import ModalSwarmCoordinator, setup_web3
from hivemind_exp.dht_utils import *
from hivemind_exp.name_utils import *
from . import global_dht
from .dht_pub import GossipDHTPublisher, RewardsDHTPublisher
from .kinesis import Kinesis
# Vikey call function
def call_vikey(prompt):
headers = {
"Authorization": f"Bearer {os.getenv('VIKEY_API_KEY')}",
"Content-Type": "application/json"
}
payload = {
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": prompt}
],
"temperature": 0.7,
"max_tokens": 1024
}
try:
response = requests.post("http://localhost:11434/v1/chat/completions", json=payload, headers=headers)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
except Exception as e:
return f"Vikey error: {str(e)}"
# UI is served from the filesystem
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DIST_DIR = os.path.join(BASE_DIR, "ui", "dist")
index_html = None
async def load_index_html():
global index_html
if index_html is None:
index_path = os.path.join(BASE_DIR, "ui", "dist", "index.html")
async with aiofiles.open(index_path, mode="r") as f:
index_html = await f.read()
class CustomJsonFormatter(jsonlogger.JsonFormatter):
def add_fields(self, log_record, record, message):
# Ensure that 'extra' fields are included in the log record
super().add_fields(log_record, record, message)
# Include both adapter extra fields and log call extra fields
if hasattr(record, "extra_fields"):
for key, value in record.extra_fields.items():
log_record[key] = value
json_formatter = CustomJsonFormatter("%(asctime)s %(levelname)s %(message)s")
# Configure the root logger
root_logger = logging.getLogger()
handler = logging.StreamHandler()
handler.setFormatter(json_formatter)
root_logger.addHandler(handler)
root_logger.setLevel(logging.INFO)
# Get the module logger
logger = logging.getLogger(__name__)
app = FastAPI()
port = os.getenv("SWARM_UI_PORT", "8000")
try:
port = int(port)
except ValueError:
logger.warning(f"invalid port {port}. Defaulting to 8000")
port = 8000
config = uvicorn.Config(
app,
host="0.0.0.0",
port=port,
timeout_keep_alive=10,
timeout_graceful_shutdown=10,
h11_max_incomplete_event_size=8192, # Max header size in bytes
)
server = uvicorn.Server(config)
@app.exception_handler(Exception)
async def internal_server_error_handler(request: Request, exc: Exception):
logger.error(f"Internal server error: {exc}")
return JSONResponse(
status_code=500,
content={
"detail": "Internal Server Error",
"message": str(exc),
},
)
@app.get("/api/healthz")
async def get_health():
lpt = global_dht.dht_cache.get_last_polled()
if lpt is None:
raise HTTPException(status_code=500, detail="dht never polled")
diff = datetime.now() - lpt
if diff > timedelta(minutes=5):
raise HTTPException(status_code=500, detail="dht last poll exceeded 5 minutes")
return {
"message": "OK",
"lastPolled": diff,
}
@app.post("/api/infer")
async def infer_vikey(request: Request):
body = await request.json()
prompt = body.get("prompt", "")
if not prompt:
raise HTTPException(status_code=400, detail="Missing prompt")
output = call_vikey(prompt)
return {"response": output}
@app.get("/api/round_and_stage")
def get_round_and_stage():
r, s = global_dht.dht_cache.get_round_and_stage()
return {
"round": r,
"stage": s,
}
@app.get("/api/leaderboard")
def get_leaderboard():
leaderboard = global_dht.dht_cache.get_leaderboard()
res = dict(leaderboard)
if res is not None:
return {
"leaders": res.get("leaders", []),
"total": res.get("total", 0),
}
@app.get("/api/leaderboard-cumulative")
def get_leaderboard_cumulative():
leaderboard = global_dht.dht_cache.get_leaderboard_cumulative()
res = dict(leaderboard)
if res is not None:
return {
"leaders": res.get("leaders", []),
"total": res.get("total", 0),
}
else:
return {
"leaders": [],
"total": 0,
}
@app.get("/api/rewards-history")
def get_rewards_history():
leaderboard = global_dht.dht_cache.get_leaderboard()
res = dict(leaderboard)
if res is not None:
return {
"leaders": res.get("rewardsHistory", []),
}
@app.get("/api/name-to-id")
def get_id_from_name(name: str = Query("")):
leaderboard = global_dht.dht_cache.get_leaderboard()
leader_ids = [leader["id"] for leader in leaderboard["leaders"]] or []
peer_id = search_peer_ids_for_name(leader_ids, name)
return {
"id": peer_id,
}
@app.post("/api/id-to-name")
async def id_to_name(request: Request):
# Check request body size (100KB limit)
content_length = request.headers.get("content-length")
if content_length and int(content_length) > 100 * 1024: # 100KB in bytes
raise HTTPException(
status_code=413, detail="Request body too large. Maximum size is 100KB."
)
# Parse request body
try:
body = await request.json()
if not isinstance(body, list):
raise HTTPException(
status_code=400, detail="Request body must be a list of peer IDs"
)
except json.JSONDecodeError as e:
raise HTTPException(status_code=400, detail=f"Invalid JSON: {str(e)}")
except Exception as e:
raise HTTPException(status_code=400, detail=f"Invalid request body: {str(e)}")
# Validate input size
if len(body) > 1000: # Limit number of IDs that can be processed
raise HTTPException(
status_code=400, detail="Too many peer IDs. Maximum is 1000."
)
# Process each ID
id_to_name_map = {}
for peer_id in body:
try:
name = get_name_from_peer_id(peer_id)
if name is not None:
id_to_name_map[peer_id] = name
except Exception as e:
logger.error(f"Error looking up name for peer ID {peer_id}: {str(e)}")
return id_to_name_map
@app.get("/api/gossip")
def get_gossip():
gs = global_dht.dht_cache.get_gossips()
return dict(gs)
if os.getenv("API_ENV") != "dev":
app.mount(
"/assets",
StaticFiles(directory=os.path.join(DIST_DIR, "assets")),
name="assets",
)
app.mount(
"/fonts", StaticFiles(directory=os.path.join(DIST_DIR, "fonts")), name="fonts"
)
app.mount(
"/images",
StaticFiles(directory=os.path.join(DIST_DIR, "images")),
name="images",
)
@app.get("/{full_path:path}")
async def catch_all(full_path: str, request: Request):
# Development reverse proxies to ui dev server
if os.getenv("API_ENV") == "dev":
logger.info(
f"proxying {full_path} into local UI development environment on 5173..."
)
async with httpx.AsyncClient() as client:
resp = await client.get(
url=f"http://localhost:5173/{full_path}", headers=request.headers
)
headers = {
k: v
for k, v in resp.headers.items()
if k.lower() not in ["content-length", "transfer-encoding"]
}
return Response(
content=resp.content, status_code=resp.status_code, headers=headers
)
# Live environment (serve from dist)
await load_index_html()
return HTMLResponse(
content=index_html,
headers={
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
"Pragma": "no-cache",
"Expires": "0",
},
)
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument(
"-ip", "--initial_peers", help="initial peers", nargs="+", type=str, default=[]
)
return parser.parse_args()
def populate_cache():
logger.info("populate_cache initialized")
try:
while True:
logger.info("pulling latest dht data...")
global_dht.dht_cache.poll_dht()
time.sleep(10)
logger.info("dht polled")
except Exception as e:
logger.error("uncaught exception while polling dht", e)
def main(args):
coordinator = ModalSwarmCoordinator(
"", web3=setup_web3()
) # Only allows contract calls
initial_peers = coordinator.get_bootnodes()
# Supplied with the bootstrap node, the client will have access to the DHT.
logger.info(f"initializing DHT with peers {initial_peers}")
kinesis_stream = os.getenv("KINESIS_STREAM", "")
kinesis_client = Kinesis(kinesis_stream)
global_dht.setup_global_dht(initial_peers, coordinator, logger, kinesis_client)
thread = Thread(target=populate_cache)
thread.daemon = True
thread.start()
# Start publishing to kinesis. This will eventually replace the populate_cache thread.
logger.info("Starting rewards publisher")
rewards_publisher = RewardsDHTPublisher(
dht=global_dht.dht,
kinesis_client=kinesis_client,
logger=logger,
coordinator=coordinator,
poll_interval_seconds=300, # 5 minute
)
rewards_publisher.start()
logger.info("Starting gossip publisher")
gossip_publisher = GossipDHTPublisher(
dht=global_dht.dht,
kinesis_client=kinesis_client,
logger=logger,
coordinator=coordinator,
poll_interval_seconds=150, # 2.5 minute
)
gossip_publisher.start()
logger.info(f"initializing server on port {port}")
server.run()
if __name__ == "__main__":
main(parse_arguments())
Dari folder root project Gensyn (yang ada docker-compose.yaml):
docker-compose build
docker-compose up -d
Ini akan menjalankan FastAPI yang bisa kamu akses di:
http://localhost:8080/api/infer
cd /root/rl-swarm
pip install boto3 pip install aiofiles httpx uvicorn fastapi python-json-logger requests pip install -r requirements.txt
cd web/ui npm install npm run build:testnet
cd /root/rl-swarm python -m web.api.server
or
#!/bin/bash
ROOT=$PWD
RED='\033[0;31m'
GREEN='\033[0;32m'
PURPLE='\033[0;95m'
BLUE='\033[0;94m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
export PUB_MULTI_ADDRS
export PEER_MULTI_ADDRS
export HOST_MULTI_ADDRS
export IDENTITY_PATH
export ORG_ID
export HF_HUB_DOWNLOAD_TIMEOUT=120
DEFAULT_PUB_MULTI_ADDRS=""
PUB_MULTI_ADDRS=${PUB_MULTI_ADDRS:-$DEFAULT_PUB_MULTI_ADDRS}
DEFAULT_PEER_MULTI_ADDRS="/ip4/38.101.215.13/tcp/30002/p2p/QmQ2gEXoPJg6iMBSUFWGzAabS2VhnzuS782Y637hGjfsRJ"
PEER_MULTI_ADDRS=${PEER_MULTI_ADDRS:-$DEFAULT_PEER_MULTI_ADDRS}
DEFAULT_HOST_MULTI_ADDRS="/ip4/0.0.0.0/tcp/38331"
HOST_MULTI_ADDRS=${HOST_MULTI_ADDRS:-$DEFAULT_HOST_MULTI_ADDRS}
DEFAULT_IDENTITY_PATH="$ROOT"/swarm.pem
IDENTITY_PATH=${IDENTITY_PATH:-$DEFAULT_IDENTITY_PATH}
cleanup() {
echo -e "${YELLOW}${BOLD}[β] Shutting down processes...${NC}"
kill $SERVER_PID 2>/dev/null || true
kill $TUNNEL_PID 2>/dev/null || true
exit 0
}
trap cleanup INT
if [ -f "modal-login/temp-data/userData.json" ]; then
cd modal-login
echo -e "\n${CYAN}${BOLD}[β] Installing dependencies with npm. This may take a few minutes, depending on your internet speed...${NC}"
npm install --legacy-peer-deps
echo -e "\n${CYAN}${BOLD}[β] Starting the development server...${NC}"
pid=$(lsof -ti:3000); if [ -n "$pid" ]; then kill -9 $pid; fi
sleep 3
npm run dev > server.log 2>&1 &
SERVER_PID=$!
MAX_WAIT=60
for ((i = 0; i < MAX_WAIT; i++)); do
if grep -q "Local: http://localhost:" server.log; then
PORT=$(grep "Local: http://localhost:" server.log | sed -n 's/.*http:\/\/localhost:\([0-9]*\).*/\1/p')
if [ -n "$PORT" ]; then
echo -e "${GREEN}${BOLD}[β] Server is running successfully on port $PORT.${NC}"
break
fi
fi
sleep 1
done
if [ $i -eq $MAX_WAIT ]; then
echo -e "${RED}${BOLD}[β] Timeout waiting for server to start.${NC}"
kill $SERVER_PID 2>/dev/null || true
exit 1
fi
cd ..
ORG_ID=$(awk 'BEGIN { FS = "\"" } !/^[ \t]*[{}]/ { print $(NF - 1); exit }' modal-login/temp-data/userData.json)
echo -e "\n${CYAN}${BOLD}[β] ORG_ID has been set to: ${BOLD}$ORG_ID\n${NC}"
else
echo -e "${RED}${BOLD}[β] modal-login/temp-data/userData.json not found! Please log in through modal first.${NC}"
exit 1
fi
echo -e "${CYAN}${BOLD}[β] Installing required Python packages, may take few mins depending on your internet speed...${NC}"
pip install --disable-pip-version-check -q -r "$ROOT"/requirements-hivemind.txt > /dev/null
pip install --disable-pip-version-check -q -r "$ROOT"/requirements.txt > /dev/null
echo -e "${GREEN}${BOLD}>>> All packages installed successfully!\n${NC}"
# Vikey AI configuration and dependencies
VikeyAI_PATH="$ROOT/vikeyai"
if [ ! -d "$VikeyAI_PATH" ]; then
echo -e "${YELLOW}[β] Cloning Vikey AI repository...${NC}"
git clone https://github.com/your-vikey-ai-repo.git "$VikeyAI_PATH"
fi
echo -e "${CYAN}[β] Installing Vikey AI dependencies...${NC}"
cd "$VikeyAI_PATH"
pip install -r requirements.txt
cd ..
# Setup configuration for Vikey AI or use GPU if available
if [ -z "$CONFIG_PATH" ]; then
if command -v nvidia-smi &> /dev/null || [ -d "/proc/driver/nvidia" ]; then
echo -e "${GREEN}${BOLD}[β] GPU detected, using GPU configuration${NC}"
CONFIG_PATH="$VikeyAI_PATH/configs/gpu/vikey_config_gpu.yaml"
echo -e "${CYAN}${BOLD}[β] Config file : ${BOLD}$CONFIG_PATH\n${NC}"
else
echo -e "${YELLOW}${BOLD}[β] No GPU detected, using CPU configuration${NC}"
CONFIG_PATH="$VikeyAI_PATH/configs/cpu/vikey_config_cpu.yaml"
echo -e "${CYAN}${BOLD}[β] Config file : ${BOLD}$CONFIG_PATH\n${NC}"
fi
fi
# Vikey AI integration and inference
echo -e "${CYAN}${BOLD}[β] Starting Vikey AI inference with the provided configuration...${NC}"
python "$VikeyAI_PATH/run_inference.py" --config "$CONFIG_PATH" --org_id "$ORG_ID" --identity_path "$IDENTITY_PATH"
# Training session with Vikey AI integrated
echo -e "${CYAN}${BOLD}[β] Starting Vikey AI training session...${NC}"
python -m hivemind_exp.gsm8k.train_single_gpu \
--hf_token "$HUGGINGFACE_ACCESS_TOKEN" \
--identity_path "$IDENTITY_PATH" \
--modal_org_id "$ORG_ID" \
--config "$CONFIG_PATH"
echo -e "${GREEN}${BOLD}[β] Vikey AI training session complete. Good luck with your project!${NC}"
