게시판바둑 on-policy 학습: 대국 로그를 KataGo 라벨로 바꾸기예제 소스

예제 소스

바둑 on-policy 학습: 대국 로그를 KataGo 라벨로 바꾸기 · 파일 169개

vlmClient.ts

/** Browser client for the MariaGO WebSocket bridge. */

import { getSelectedVlmModelId } from "@/lib/vlmModels";

export type VlmMove =
  | { ok: true; pass: true }
  | { ok: true; pass: false; row: number; col: number }
  | { ok: false; error: string };

/**
 * HTTPS(baduk.mariago.kr)에서는 브라우저가 ws:// 를 막으므로
 * nginx `/vlm-ws` → :9320 프록시를 통해 wss 동일 오리진으로 연결한다.
 * 로컬 http는 기존처럼 hostname:9320.
 * 오버라이드: localStorage.mariago_ws
 */
export function getBridgeUrl(): string {
  if (typeof window === "undefined") return "ws://127.0.0.1:9320";
  const override = localStorage.getItem("mariago_ws");
  if (override) return override;
  if (window.location.protocol === "https:") {
    return `wss://${window.location.host}/vlm-ws`;
  }
  return `ws://${window.location.hostname}:9320`;
}

export async function requestVlmMove(opts: {
  board: number[];
  aiSide: 1 | 2;
  gameEpoch: number;
  rejects?: Array<[number, number]>;
  timeoutMs?: number;
  engine?: string;
  ko?: [number, number] | null;
}): Promise<VlmMove> {
  const url = getBridgeUrl();
  const timeoutMs = opts.timeoutMs ?? 240_000;

  return new Promise((resolve) => {
    let settled = false;
    const finish = (v: VlmMove) => {
      if (settled) return;
      settled = true;
      try {
        ws.close();
      } catch {
        /* ignore */
      }
      resolve(v);
    };

    let ws: WebSocket;
    try {
      ws = new WebSocket(url);
    } catch (e) {
      finish({ ok: false, error: `WebSocket 연결 실패: ${String(e)}` });
      return;
    }

    const timer = setTimeout(() => {
      finish({ ok: false, error: "VLM 응답 시간 초과" });
    }, timeoutMs);

    ws.onopen = () => {
      ws.send(
        JSON.stringify({
          type: "move",
          board: opts.board,
          ai_side: opts.aiSide,
          game_epoch: opts.gameEpoch,
          rejects: opts.rejects ?? [],
          engine: opts.engine || getSelectedVlmModelId(),
          ko: opts.ko ?? null,
        }),
      );
    };

    ws.onmessage = (ev) => {
      clearTimeout(timer);
      try {
        const msg = JSON.parse(String(ev.data));
        if (msg.type === "error") {
          finish({ ok: false, error: msg.error || "서버 오류" });
          return;
        }
        if (msg.type === "move") {
          if (msg.pass) finish({ ok: true, pass: true });
          else finish({ ok: true, pass: false, row: msg.row, col: msg.col });
          return;
        }
        finish({ ok: false, error: "알 수 없는 응답" });
      } catch (e) {
        finish({ ok: false, error: String(e) });
      }
    };

    ws.onerror = () => {
      clearTimeout(timer);
      finish({
        ok: false,
        error: `브리지 연결 실패 (${url}). scripts/mariago_ws_bridge.py 실행 여부를 확인하세요.`,
      });
    };
  });
}