From 72d3d37231e922a6fa9afca16e08fa45842d5eb4 Mon Sep 17 00:00:00 2001 From: JustVugg Date: Thu, 16 Jul 2026 19:39:40 +0200 Subject: [PATCH 1/2] ci: run the checks on main too (#144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @ZacharyZcR's workflow, already green on dev (78c77bf): engine + C suite, web build + vitest, python suite, and a real CUDA syntax check. Only the workflow file is cherry-picked here — main's engine code stays at 54cfe56 and nothing else from dev comes along. main is where the checks are worth the most and where they were missing entirely. Includes the two fixes the first run exposed: the pinned action's version table stops at 12.6.2 (12.6.3 failed the install), and `nvcc ... | head -40` took its exit status from head, so the job could never fail. Co-Authored-By: ZacharyZcR <#144> Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci.yml | 72 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e18f1b3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,72 @@ +name: CI + +on: + pull_request: + branches: [dev, main] + push: + branches: [dev, main] + +jobs: + engine: + name: Engine (Linux, CPU) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build glm + run: cd c && make glm + - name: C test suite + run: cd c && make test-c + + engine-cuda-syntax: + name: CUDA syntax check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install CUDA toolkit (compiler only) + uses: Jimver/cuda-toolkit@v0.2.19 + with: + # v0.2.19's version table stops at 12.6.2 — 12.6.3 fails the install + # step with "Version not available" before nvcc is ever reached. + cuda: '12.6.2' + method: network + sub-packages: '["nvcc"]' + - name: Compile backend_cuda.cu (syntax only, no GPU) + run: | + cd c + # NOT `nvcc ... | head -40`: a pipeline exits with the status of its + # LAST command, so head's 0 masked every nvcc error and the job could + # only ever be green. A check that cannot fail is worse than no check — + # it buys false confidence in the one file nothing else compiles. + nvcc -O2 -std=c++17 -arch=sm_80 -c backend_cuda.cu -o /dev/null \ + -Xcompiler=-Wall,-Wextra + echo "CUDA syntax check passed" + + web: + name: Web UI + runs-on: ubuntu-latest + defaults: + run: + working-directory: web + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + cache-dependency-path: web/package-lock.json + - run: npm ci + - name: Build + run: npm run build + - name: Test + run: npx vitest run + + python: + name: Python tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Python test suite + run: cd c && python3 -m unittest discover -s tests -p 'test_*.py' From ef97f3cf2b0a6f411de0ff02ae9bd0fba2d11923 Mon Sep 17 00:00:00 2001 From: volognamor Date: Fri, 17 Jul 2026 21:49:28 +0300 Subject: [PATCH 2/2] Windows: fix non-ASCII chat prompt corruption (ANSI codepage vs UTF-8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit coli passes the chat prompt to glm.exe through the PROMPT/COLI_PROMPT environment variable. On Windows, plain getenv() is populated by the CRT from the ANSI-codepage view of the environment block, not UTF-8 — so any non-ASCII prompt text (Cyrillic, CJK, ...) is silently mangled before the byte-level tokenizer ever sees it, even though the parent process (coli's Python subprocess call) sets the value correctly via the wide env block. Add compat_getenv_utf8() in compat.h: reads the variable through GetEnvironmentVariableW and converts straight to UTF-8, bypassing the ANSI codepage entirely. No-op passthrough to getenv() on non-Windows platforms. coli_user_prompt() in glm.c now uses it for both COLI_PROMPT and PROMPT. Verified: tiny-oracle self-test still 32/32 after rebuild, and a real run against the full GLM-5.2-int4 model with a Cyrillic prompt now round-trips correctly end to end (input echoed intact, coherent Cyrillic output), where it previously produced replacement-character garbage on both input and output. --- c/compat.h | 31 +++++++++++++++++++++++++++++++ c/glm.c | 4 ++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/c/compat.h b/c/compat.h index 82de8b6..06779e0 100644 --- a/c/compat.h +++ b/c/compat.h @@ -304,8 +304,39 @@ static inline int compat_setenv(const char *name, const char *value, int overwri } #define setenv(name,value,overwrite) compat_setenv(name,value,overwrite) +/* --- getenv_utf8: read an env var as UTF-8, not through the ANSI codepage --- + * Plain getenv()/_environ are populated by the CRT from the ANSI-codepage view + * of the process environment block, not UTF-8. A parent that hands the child a + * Unicode value via CreateProcessW's wide env block (e.g. Python's subprocess + * module, which coli uses to pass the chat prompt) round-trips correctly only + * through GetEnvironmentVariableW; going through narrow getenv() re-encodes it + * via CP_ACP first, so any non-ASCII prompt text (Cyrillic, CJK, ...) comes out + * corrupted before the byte-level tokenizer ever sees it. Read the wide value + * directly and convert straight to UTF-8, bypassing the ANSI codepage entirely. + * Returned buffer is intentionally leaked: called a handful of times at + * startup, lives for the process. */ +static inline const char *compat_getenv_utf8(const char *name){ + wchar_t wname[64]; + if(MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, 64) <= 0) return getenv(name); + DWORD need = GetEnvironmentVariableW(wname, NULL, 0); + if(!need) return NULL; + wchar_t *wval = (wchar_t*)malloc(need * sizeof(wchar_t)); + if(!wval) return NULL; + GetEnvironmentVariableW(wname, wval, need); + int blen = WideCharToMultiByte(CP_UTF8, 0, wval, -1, NULL, 0, NULL, NULL); + char *val = blen>0 ? (char*)malloc((size_t)blen) : NULL; + if(val) WideCharToMultiByte(CP_UTF8, 0, wval, -1, val, blen, NULL, NULL); + free(wval); + return val; +} +#define getenv_utf8(name) compat_getenv_utf8(name) + #endif /* _WIN32 */ +#ifndef getenv_utf8 +#define getenv_utf8(name) getenv(name) +#endif + /* --- compat_aligned_free su piattaforme diverse da Windows --- * Su Linux/macOS, posix_memalign usa free() normale. */ #ifndef compat_aligned_free diff --git a/c/glm.c b/c/glm.c index 00c7076..b0e419c 100644 --- a/c/glm.c +++ b/c/glm.c @@ -5701,9 +5701,9 @@ static void cap_for_ram(Model *m, double ram_gb, int ebits, int max_ctx){ * self-test, and would "generate" from "$P$G". So on Windows a PROMPT carrying * cmd's $-metacodes is ignored; set COLI_PROMPT to pass a real prompt from cmd. */ static const char *coli_user_prompt(void){ - const char *p = getenv("COLI_PROMPT"); + const char *p = getenv_utf8("COLI_PROMPT"); if(p) return p; - p = getenv("PROMPT"); + p = getenv_utf8("PROMPT"); #ifdef _WIN32 if(p) for(const char *q=p; q[0]; q++) if(q[0]=='$' && q[1] && strchr("ABCDEFGHLNPQSTV_+|$", q[1]&~0x20)){ p=NULL; break; }