Gensyn

Gensyn

1. Clone dan Jalankan ViKey Inference

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

2. Jalankan ViKey Inference di Background

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

3. Nonaktifkan Inference Lokal di Gensyn

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.

4. Tambahkan Proxy API ke Vikey

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.


πŸ”§ Edit web/api/server.py β†’ Tambahkan ini:

  1. 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())

5. Build dan Jalankan Docker

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}"