# Gensyn

By [antisav.eth](https://paragraph.com/@antisav) · 2025-04-25

---

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

---

*Originally published on [antisav.eth](https://paragraph.com/@antisav/gensyn)*
