diff --git a/cmd/cyrene/main.go b/cmd/cyrene/main.go index 90121a7..260bf59 100644 --- a/cmd/cyrene/main.go +++ b/cmd/cyrene/main.go @@ -17,6 +17,12 @@ import ( "os" "path/filepath" "time" + // Embed the IANA timezone database so the configured TZ (config.TZ, default + // "Asia/Shanghai") resolves everywhere: Windows ships no system tzdata and Go + // deliberately does not bundle one, so without this the loop's time injection + // silently disables itself there (internal/runtime/loop/time.go logs + // "timezone load failed"). Costs ~450 KB and keeps the binary self-contained. + _ "time/tzdata" capreg "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry" "git.yeij.top/AskaEth/Cyrene_v2/internal/config" diff --git a/scripts/embed.bat b/scripts/embed.bat new file mode 100644 index 0000000..7f6fb9b --- /dev/null +++ b/scripts/embed.bat @@ -0,0 +1,134 @@ +@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" +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','127.0.0.1','--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