@echo off setlocal EnableExtensions EnableDelayedExpansion rem ============================================================ rem Cyrene v2 local embedding server (llama.cpp) - Windows rem rem Usage: rem scripts\embed.bat start (default) rem scripts\embed.bat start start llama-server on :11434 rem scripts\embed.bat stop stop llama-server rem scripts\embed.bat status show status rem rem Serves an OpenAI-compatible embeddings endpoint: rem POST http://127.0.0.1:/v1/embeddings rem backed by Qwen3-Embedding-0.6B Q8_0 (dim 1024, CPU) - the model rem .env EMBEDDING_API_URL / EMBEDDING_MODEL are expected to point at. rem Cyrene only takes the real semantic path when this is up; otherwise it rem logs "vector index unavailable, falling back to in-memory semantic". rem rem The vector index itself lives in PostgreSQL + pgvector (DB_PG_URL): rem docker compose -f docker-compose.dev.yml up -d pg rem rem Overridable via environment: rem LLAMA_DIR (default %USERPROFILE%\apps\llama.cpp) rem EMBED_MODEL (default %USERPROFILE%\.models\Qwen3-Embedding-0.6B-Q8_0.gguf) rem EMBED_PORT (default 11434) rem Logs: %USERPROFILE%\llama.log (+ llama.stdout.log) rem rem ASCII-only + CRLF on purpose (cmd parsing is codepage/line-ending rem sensitive); Windows tools are called by absolute System32 path so an rem MSYS/MinGW PATH (Git Bash) cannot shadow them. rem ============================================================ set "SYS=%SystemRoot%\System32" if not defined LLAMA_DIR set "LLAMA_DIR=%USERPROFILE%\apps\llama.cpp" if not defined EMBED_MODEL set "EMBED_MODEL=%USERPROFILE%\.models\Qwen3-Embedding-0.6B-Q8_0.gguf" if not defined EMBED_PORT set "EMBED_PORT=11434" rem EMBED_HOST: which interface llama-server binds to. Default 127.0.0.1 rem (loopback only). History, so nobody repeats the experiment: rem 2026-10-07 the owner briefly asked for 0.0.0.0; it was measured and then rem reverted, because (a) 0.0.0.0 is an IPv4 wildcard and does NOT cover rem IPv6 -- with --host 0.0.0.0, netstat showed only "TCP 0.0.0.0:11434 rem LISTENING" (no IPv6 entry) and http://[::1]:11434 still failed, so it rem did NOT fix the client-side hang it was meant to fix; and (b) rem llama-server has NO auth, so 0.0.0.0 exposes this machine's embedding rem compute to every device on the LAN. rem Clients must dial 127.0.0.1 (see internal/runtime/embedding). if not defined EMBED_HOST set "EMBED_HOST=127.0.0.1" set "SERVER=%LLAMA_DIR%\llama-server.exe" set "LOG_FILE=%USERPROFILE%\llama.log" set "STDOUT_FILE=%USERPROFILE%\llama.stdout.log" set "HEALTH_URL=http://127.0.0.1:%EMBED_PORT%/health" set "ACTION=%~1" if "%ACTION%"=="" set "ACTION=start" if /i "%ACTION%"=="start" goto :do_start if /i "%ACTION%"=="stop" goto :do_stop if /i "%ACTION%"=="status" goto :do_status echo Usage: scripts\embed.bat [start^|stop^|status] (no arg = start) exit /b 1 :do_start if not exist "%SYS%\curl.exe" ( echo [ERROR] %SYS%\curl.exe not found - the readiness check needs it. exit /b 1 ) call :is_running if defined RUNNING_PID ( echo embedding server is already running ^(PID !RUNNING_PID!^) on port %EMBED_PORT%. exit /b 0 ) if not exist "%SERVER%" ( echo [ERROR] llama-server.exe not found: %SERVER% echo download the llama.cpp bin-win-cpu-x64 zip and extract it there. exit /b 1 ) if not exist "%EMBED_MODEL%" ( echo [ERROR] embedding model not found: %EMBED_MODEL% echo fetch Qwen3-Embedding-0.6B-Q8_0.gguf from ModelScope. exit /b 1 ) echo Starting llama-server -^> %EMBED_MODEL% ^(port %EMBED_PORT%^) powershell -NoProfile -Command "Start-Process -FilePath '%SERVER%' -ArgumentList '-m','%EMBED_MODEL%','--embeddings','--host','%EMBED_HOST%','--port','%EMBED_PORT%','-c','4096','-b','2048','-ub','2048','-t','8' -WorkingDirectory '%LLAMA_DIR%' -RedirectStandardOutput '%STDOUT_FILE%' -RedirectStandardError '%LOG_FILE%' -WindowStyle Hidden" set /a TRIES=0 :start_wait set /a TRIES+=1 call :healthy if not errorlevel 1 ( echo Ready: http://127.0.0.1:%EMBED_PORT%/v1/embeddings ^(log: %LOG_FILE%^) exit /b 0 ) if !TRIES! GEQ 30 ( echo [ERROR] not ready after ~60s - check %LOG_FILE% exit /b 1 ) "%SYS%\ping.exe" -n 2 127.0.0.1 >nul goto :start_wait :do_stop call :is_running if not defined RUNNING_PID ( echo embedding server is not running. exit /b 0 ) echo Stopping PID !RUNNING_PID! ... "%SYS%\taskkill.exe" /PID !RUNNING_PID! /T /F >nul 2>&1 "%SYS%\ping.exe" -n 2 127.0.0.1 >nul call :is_running if not defined RUNNING_PID ( echo Stopped. exit /b 0 ) echo [WARN] still listening on %EMBED_PORT%. exit /b 1 :do_status call :is_running call :healthy if not errorlevel 1 ( echo embedding: running ^(PID !RUNNING_PID!^) . port %EMBED_PORT% . health ok exit /b 0 ) if defined RUNNING_PID ( echo embedding: process alive ^(PID !RUNNING_PID!^) but health not ready ^(%HEALTH_URL%^) exit /b 1 ) echo embedding: not running exit /b 1 rem healthy: errorlevel 0 = HTTP 2xx :healthy "%SYS%\curl.exe" -s -f --max-time 3 -o NUL "%HEALTH_URL%" >nul 2>&1 exit /b %errorlevel% rem is_running -> RUNNING_PID (detected from the listening port) :is_running set "RUNNING_PID=" set "NS_FILE=%TEMP%\embed-netstat-%RANDOM%.txt" "%SYS%\netstat.exe" -ano -p tcp > "%NS_FILE%" 2>nul for /f "tokens=2,5" %%A in ('%SYS%\findstr.exe /C:"LISTENING" "%NS_FILE%"') do ( if not defined RUNNING_PID ( echo %%A| "%SYS%\findstr.exe" /E /C:":%EMBED_PORT%" >nul && set "RUNNING_PID=%%B" ) ) del "%NS_FILE%" >nul 2>&1 exit /b 0