Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
407 lines (362 loc) · 20.3 KB
/
Copy pathMakefile
File metadata and controls
407 lines (362 loc) · 20.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
.PHONY: deps build remake build-with-webui build-cuda build-rocm \
build-sycl build-vulkan rebuild clean reset \
test test-fast test-slow test-bench test-bench-fast smoke \
install uninstall release-notes bump-check test-db-migrate \
test-golden combine test-external-smoke test-external-oop \
bindings test-bindings test-bindings-pytest wheel clean-bindings \
agent-hax agent-hax-default
# Override with `make PYTHON=<cmd>` for
# unusual environments (e.g. `PYTHON="uv run python"`).
PYTHON ?= $(shell \
if python3 --version >/dev/null 2>&1; then echo python3; \
elif python --version >/dev/null 2>&1; then echo python; \
elif py -3 --version >/dev/null 2>&1; then echo py -3; \
else echo python3; fi)
BUILD_DIR ?= build
PREFIX ?= /usr/local
DESTDIR ?=
# When a CMAKE_<LANG>_COMPILER_LAUNCHER is set in the environment (e.g. CI
# exporting ccache), forward it to chimera's own configure as -D flags.
# CMake does not read these from the environment, so without this the
# launcher only applies to the deps build (manage.py forwards it there);
# this covers chimera's own TUs too. Expands to nothing when unset, so a
# normal `make build` is unchanged. (Env vars are visible as make vars.)
CMAKE_LAUNCHER_FLAGS := \
$(if $(CMAKE_C_COMPILER_LAUNCHER),-DCMAKE_C_COMPILER_LAUNCHER=$(CMAKE_C_COMPILER_LAUNCHER)) \
$(if $(CMAKE_CXX_COMPILER_LAUNCHER),-DCMAKE_CXX_COMPILER_LAUNCHER=$(CMAKE_CXX_COMPILER_LAUNCHER)) \
$(if $(CMAKE_CUDA_COMPILER_LAUNCHER),-DCMAKE_CUDA_COMPILER_LAUNCHER=$(CMAKE_CUDA_COMPILER_LAUNCHER)) \
$(if $(CMAKE_HIP_COMPILER_LAUNCHER),-DCMAKE_HIP_COMPILER_LAUNCHER=$(CMAKE_HIP_COMPILER_LAUNCHER))
build: deps
@cmake -S . -B $(BUILD_DIR) -DSD_USE_VENDORED_GGML=OFF -DCMAKE_BUILD_TYPE=Release $(CMAKE_LAUNCHER_FLAGS)
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
remake: reset build test
# build-with-webui: same as `build`, but flips the experimental
# CHIMERA_WEBUI_EMBED option ON so the chimera binary bakes upstream's
# prebuilt web UI bundle into itself (GET / + /bundle.{js,css}) via the
# generated ui.cpp. Adds ~3 MB (gzip-compressed) to the binary.
#
# IMPORTANT (llama.cpp b9318+): upstream no longer ships prebuilt assets
# in the source tree, so `deps` alone does NOT stage them and this target
# will FATAL-error at configure. Produce + stage the assets first:
# (cd build/llama.cpp/tools/ui && npm install && npm run build)
# python3 scripts/manage.py build --llama-cpp --sd-shared-ggml
# then run `make build-with-webui`. A Node toolchain is required for that
# one-time asset build. See docs/dev/webui.md sections 2.1 and 10 for the
# full picture. Pass --no-webui at runtime to suppress per-invocation.
build-with-webui: deps
@cmake -S . -B $(BUILD_DIR) -DSD_USE_VENDORED_GGML=OFF -DCMAKE_BUILD_TYPE=Release -DCHIMERA_WEBUI_EMBED=ON $(CMAKE_LAUNCHER_FLAGS)
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
# GPU-backend build targets. Each one builds the third-party deps with the
# matching GGML_<BACKEND>=1 env var (picked up by scripts/manage.py, which
# forwards it to llama.cpp / whisper.cpp / stable-diffusion.cpp configures)
# and then configures chimera with -DGGML_<BACKEND>=ON so the resulting
# binary links the backend's static archive. Backend toolkit (CUDA Toolkit,
# ROCm/HIP, oneAPI, Vulkan SDK) must already be installed on the host.
#
# Override architectures via env vars:
# CMAKE_CUDA_ARCHITECTURES=89 (Ada/RTX 40xx; default builds many)
# CMAKE_HIP_ARCHITECTURES=gfx1100 (RDNA3; default builds many — ROCm
# uses HIP under the hood, so the
# env var keeps its upstream name)
# Override toolchains via env vars when not on PATH:
# CMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc
# CUDA perf knobs (forwarded by manage.py): GGML_CUDA_FORCE_MMQ,
# GGML_CUDA_FORCE_CUBLAS, GGML_CUDA_FA_ALL_QUANTS, GGML_CUDA_PEER_MAX_BATCH_SIZE.
# ROCm knob: GGML_HIP_ROCWMMA_FATTN=1 for rocWMMA flash attention.
#
# Verify the resulting binary picked up the backend with `./build/chimera info`.
build-cuda:
@GGML_CUDA=1 $(MAKE) deps
@cmake -S . -B $(BUILD_DIR) -DSD_USE_VENDORED_GGML=OFF -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON $(CMAKE_LAUNCHER_FLAGS)
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
build-rocm:
@GGML_HIP=1 $(MAKE) deps
@cmake -S . -B $(BUILD_DIR) -DSD_USE_VENDORED_GGML=OFF -DCMAKE_BUILD_TYPE=Release -DGGML_HIP=ON $(CMAKE_LAUNCHER_FLAGS)
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
build-sycl:
@GGML_SYCL=1 $(MAKE) deps
@cmake -S . -B $(BUILD_DIR) -DSD_USE_VENDORED_GGML=OFF -DCMAKE_BUILD_TYPE=Release -DGGML_SYCL=ON $(CMAKE_LAUNCHER_FLAGS)
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
build-vulkan:
@GGML_VULKAN=1 $(MAKE) deps
@cmake -S . -B $(BUILD_DIR) -DSD_USE_VENDORED_GGML=OFF -DCMAKE_BUILD_TYPE=Release -DGGML_VULKAN=ON $(CMAKE_LAUNCHER_FLAGS)
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
rebuild:
@cmake --build $(BUILD_DIR) --target chimera --config Release -j
# DEPS_EXTRA: extra flags forwarded verbatim to manage.py's deps build.
# Used by CI to inject e.g. --no-sd-examples (skips sd-cli/sd-server, which
# chimera does not link) to cut GPU build time. Empty by default; command-
# line overrides propagate into the nested `$(MAKE) deps` of build-cuda etc.
DEPS_EXTRA ?=
deps:
@SD_USE_VENDORED_GGML=0 $(PYTHON) scripts/manage.py build --all --deps-only --sd-shared-ggml $(DEPS_EXTRA)
test:
@$(PYTHON) scripts/test.py
# test-fast: same suite as `test` but skip the three vision / SD round-trips
# that dominate wall-clock time (vision pipeline ~165s, sd img2img ~75s,
# sd_xl_turbo ~30s). Tight inner-loop tier for edits that don't touch the
# vision or SD paths. The slow set is centralised in SLOW_TEST_RE in
# scripts/test.py — update there when a new test crosses the ~20s mark.
test-fast:
@$(PYTHON) scripts/test.py --no-slow
# test-slow: complement of test-fast. Runs only the heavy vision / SD
# round-trips. Useful when iterating on the SD pipeline or the mtmd
# vision integration without paying for the rest of the suite.
test-slow:
@$(PYTHON) scripts/test.py --slow-only
# test-bench: run the full suite and write per-test timings to a JSON
# file for later comparison. Override the destination with
# `make test-bench BENCH=mybench.json`. Companion to scripts/test_diff.py,
# which diffs two of these files and flags regressions:
#
# git checkout main && make build && make test-bench BENCH=main.json
# git checkout dev && make build && make test-bench BENCH=dev.json
# scripts/test_diff.py main.json dev.json
#
# `test-bench-fast` runs the same with --no-slow for a quick check that
# omits the heavy vision / SD round-trips.
BENCH ?= $(BUILD_DIR)/test_timings.json
test-bench:
@$(PYTHON) scripts/test.py --timings-out $(BENCH)
test-bench-fast:
@$(PYTHON) scripts/test.py --no-slow --timings-out $(BENCH)
smoke:
@$(PYTHON) scripts/test.py --smoke
install: $(BUILD_DIR)/chimera
@install -d "$(DESTDIR)$(PREFIX)/bin"
@install -m 755 "$(BUILD_DIR)/chimera" "$(DESTDIR)$(PREFIX)/bin/chimera"
@echo "installed $(DESTDIR)$(PREFIX)/bin/chimera"
uninstall:
@rm -f "$(DESTDIR)$(PREFIX)/bin/chimera"
@echo "removed $(DESTDIR)$(PREFIX)/bin/chimera"
$(BUILD_DIR)/chimera:
$(MAKE) build
clean:
@rm -rf $(BUILD_DIR)
@rm -rf bindings/build
reset: clean
@rm -rf thirdparty/llama.cpp thirdparty/whisper.cpp thirdparty/stable-diffusion.cpp thirdparty/linenoise
@rm -rf bindings/.venv
# release-notes: write release-notes.md by extracting the current
# CHIMERA_VERSION's section from CHANGELOG.md. Same script the release
# workflow runs; pulls the version straight from scripts/manage.py so
# the file always matches what the binary will report at runtime.
# Override the version with `make release-notes VERSION=X.Y.Z`.
VERSION ?= $(shell $(PYTHON) -c "import re; \
print(re.search(r'^CHIMERA_VERSION = \"([^\"]+)\"', open('scripts/manage.py').read(), re.M).group(1))")
release-notes:
@$(PYTHON) scripts/release_notes.py $(VERSION)
# bump-check: diff the currently-vendored upstream-server headers against
# llama.cpp at $(LLAMA_VERSION) (default: whatever scripts/manage.py has
# pinned). server-context.h / server-http.h aren't part of upstream's
# stable API and shift with internal refactors, so a chimera bump that
# bypasses this check can silently break the build or the runtime.
# It also fails when upstream's server.cpp registers a route chimera
# neither binds nor lists in scripts/server_routes.py:UNBOUND.
# Run before changing LLAMACPP_VERSION; the script exits non-zero when
# any of the headers changed.
LLAMA_VERSION ?=
bump-check:
@$(PYTHON) scripts/manage.py bump_check $(if $(LLAMA_VERSION),--llama-version $(LLAMA_VERSION))
# test-db-migrate: build a v1-schema chimera.db in a temp dir, drive
# `chimera db status` against it (which calls open_and_migrate), and
# assert the v1 -> latest migration both advances the schema and
# preserves pre-existing rows. Pre-empts schema-migration regressions
# across version bumps — every new migration we land has to keep
# upgrading old user DBs cleanly. Requires the chimera binary built.
test-db-migrate: $(BUILD_DIR)/chimera
@$(PYTHON) scripts/test_db_migrate.py
# combine: bundle the third-party static archives chimera transitively
# links into two grouped archives under build/:
# build/libchimera_thirdparty.a (normal-linked by consumer)
# build/libchimera_ggml.a (consumer MUST whole-archive this)
# Combined with build/libchimera.a (produced by `make build`), these
# are what a non-CMake external consumer links against. See
# doc/dev/combine_archives.md for the link contract.
combine: $(BUILD_DIR)/libchimera.a
@$(PYTHON) scripts/combine_archives.py
$(BUILD_DIR)/libchimera_thirdparty.a $(BUILD_DIR)/libchimera_ggml.a: $(BUILD_DIR)/libchimera.a
@$(PYTHON) scripts/combine_archives.py
$(BUILD_DIR)/libchimera.a:
$(MAKE) build
# test-external-smoke: build and run tests/external/chimera_smoke,
# which links the three chimera archives the way a non-CMake consumer
# would and exercises the link contract end-to-end (ggml backend
# registration, llama + chimera symbol resolution). Set
# CHIMERA_SMOKE_MODEL=<path/to/.gguf> to additionally run a full
# tokenize + llama_decode inference probe. See
# doc/dev/combine_archives.md section 7 for what this validates.
test-external-smoke: tests/external/build/chimera_smoke tests/external/build/chimera_hpp_smoke
@ctest --test-dir tests/external/build --output-on-failure
# Convenience: run only the OOP-layer lane (chimera_hpp_smoke). Same
# env-var inputs as the full target -- CHIMERA_SMOKE_MODEL gates the
# inference probe; CHIMERA_SMOKE_WHISPER_MODEL + CHIMERA_SMOKE_WHISPER_INPUT
# gate the Whisper persistence probe.
test-external-oop: tests/external/build/chimera_smoke tests/external/build/chimera_hpp_smoke
@ctest --test-dir tests/external/build -L OOP --output-on-failure
tests/external/build/chimera_smoke tests/external/build/chimera_hpp_smoke: tests/external/smoke.cpp tests/external/hpp_smoke.cpp tests/external/CMakeLists.txt $(BUILD_DIR)/libchimera.a $(BUILD_DIR)/libchimera_thirdparty.a $(BUILD_DIR)/libchimera_ggml.a
@cmake -S tests/external -B tests/external/build
@cmake --build tests/external/build
# bindings: build the nanobind Python extension (the `chimera` module) under
# bindings/ against the three prebuilt archives -- same link contract as the
# external smoke tests, but the output is a Python module rather than an
# executable. Depends on the archives, so a from-scratch `make bindings`
# builds chimera + combines the archives first. Output lands at
# bindings/build/chimera.*.so; add bindings/build to PYTHONPATH to import it.
#
# The build needs nanobind + scikit-build-core for the Python that CMake uses.
# When `uv` is available (the default path), this target provisions them into
# a local venv (BINDINGS_VENV, default bindings/.venv) and points CMake at that
# interpreter -- no system-Python pollution, works out of the box.
#
# The venv is pinned to $(PYTHON)'s interpreter (the project's Python, e.g.
# system python3) so the built module matches the Python you actually use --
# NOT uv's default managed interpreter, which `uv venv` would otherwise pick
# (it ignores a bindings/.python-version because this recipe runs from the repo
# root, not bindings/, so the module would silently target uv's default
# version). Build for a different Python with `make PYTHON=python3.13 bindings`.
#
# Override BINDINGS_PY=/path/to/python to use your own interpreter that already
# has nanobind installed (uv is then not used). With neither uv nor BINDINGS_PY,
# it falls back to $(PYTHON), which must have nanobind installed.
# See bindings/README.md.
#
# (Alternatively, skip this target and `uv pip install ./bindings` -- build
# isolation handles the toolchain. See bindings/pyproject.toml.)
BINDINGS_VENV ?= bindings/.venv
bindings: $(BUILD_DIR)/libchimera.a $(BUILD_DIR)/libchimera_thirdparty.a $(BUILD_DIR)/libchimera_ggml.a
@if [ -n "$(BINDINGS_PY)" ]; then \
py="$(BINDINGS_PY)"; \
elif command -v uv >/dev/null 2>&1; then \
pyexe="$$($(PYTHON) -c 'import sys; print(sys.executable)')"; \
if [ -d "$(BINDINGS_VENV)" ]; then \
echo "bindings: reusing $(BINDINGS_VENV) (python: $$pyexe)"; \
else \
echo "bindings: provisioning nanobind + scikit-build-core via uv into $(BINDINGS_VENV) (python: $$pyexe)"; \
uv venv "$(BINDINGS_VENV)" --python "$$pyexe" >/dev/null; \
fi; \
uv pip install --python "$(BINDINGS_VENV)/bin/python" -q nanobind scikit-build-core; \
py="$(abspath $(BINDINGS_VENV))/bin/python"; \
else \
echo "bindings: uv not found; using $(PYTHON) (must have nanobind installed)"; \
py="$$($(PYTHON) -c 'import sys; print(sys.executable)')"; \
fi; \
cmake -S bindings -B bindings/build -DCHIMERA_BUILD_ROOT="$(abspath $(BUILD_DIR))" -DPython_EXECUTABLE="$$py"
@cmake --build bindings/build
# test-bindings: build the module + run the Python smoke test. Uses the
# bindings venv interpreter when present (guaranteed ABI match), else
# $(PYTHON). CHIMERA_SMOKE_MODEL gates the optional inference probe (the
# import/exception probe always runs).
test-bindings: bindings
@py="$(PYTHON)"; \
[ -x "$(BINDINGS_VENV)/bin/python" ] && py="$(BINDINGS_VENV)/bin/python"; \
PYTHONPATH=bindings/build $$py bindings/smoke_test.py
# test-bindings-pytest: build the module + run the pytest suite under
# bindings/tests. Uses the bindings venv interpreter when present (guaranteed
# ABI match with the freshly-built .so), else $(PYTHON); installs pytest into
# that venv via uv if missing. Model-dependent tests SKIP when their model file
# is absent -- override paths with CHIMERA_TEST_* env vars (see conftest.py).
# Pass extra pytest args via PYTEST_ARGS, e.g. `make test-bindings-pytest
# PYTEST_ARGS="-k tokenizer -v"`.
test-bindings-pytest: bindings
@py="$(PYTHON)"; \
if [ -x "$(BINDINGS_VENV)/bin/python" ]; then \
py="$(BINDINGS_VENV)/bin/python"; \
if ! "$$py" -c 'import pytest' >/dev/null 2>&1; then \
if command -v uv >/dev/null 2>&1; then \
echo "test-bindings-pytest: installing pytest into $(BINDINGS_VENV)"; \
uv pip install --python "$$py" -q pytest; \
else \
echo "test-bindings-pytest: pytest missing from $(BINDINGS_VENV) and uv not found"; \
exit 1; \
fi; \
fi; \
fi; \
PYTHONPATH=bindings/build $$py -m pytest bindings/tests $(PYTEST_ARGS)
# wheel: build an installable .whl for the chimera Python bindings via
# uv + scikit-build-core. Output lands in bindings/dist/.
#
# IMPORTANT -- this is a LOCAL, host-optimized wheel, not a portable one.
# The extension statically links the three prebuilt archives, so the wheel
# is self-contained at RUNTIME (no repo / no archives needed to import it),
# but it inherits the archives' host-tuned ggml build (GGML_NATIVE, single
# backend). It runs only on this machine (or one bit-identical for the
# relevant ISA / GPU toolkit). Cross-machine redistribution is an explicit
# non-goal of the archive layer -- see docs/dev/combine_archives.md S1.
#
# Uses `uv build --wheel` (builds the wheel straight from the source tree),
# NOT a plain `uv build`. A plain `uv build` makes an sdist first and builds
# the wheel from the *extracted* sdist, where the archives + repo headers
# (which live outside bindings/) are absent, so its CMake configure fails.
# The --wheel path keeps the real repo paths intact; CHIMERA_BUILD_ROOT is
# passed explicitly so it resolves regardless of cwd. Build isolation
# auto-provisions scikit-build-core + nanobind.
#
# Build for a specific interpreter with `make PYTHON=python3.13 wheel`;
# change the output dir with `make WHEEL_OUT=dist wheel`.
WHEEL_OUT ?= bindings/dist
wheel: $(BUILD_DIR)/libchimera.a $(BUILD_DIR)/libchimera_thirdparty.a $(BUILD_DIR)/libchimera_ggml.a
@command -v uv >/dev/null 2>&1 || { \
echo "wheel: uv is required (https://docs.astral.sh/uv/); or use 'uv pip install ./bindings'"; \
exit 1; }
@pyexe="$$($(PYTHON) -c 'import sys; print(sys.executable)')"; \
echo "wheel: building host-optimized wheel for $$pyexe -> $(WHEEL_OUT)/"; \
uv build --wheel ./bindings --python "$$pyexe" -o "$(WHEEL_OUT)" \
-C cmake.define.CHIMERA_BUILD_ROOT="$(abspath $(BUILD_DIR))"
@echo "wheel: done (host-optimized; not for cross-machine redistribution)"
# clean-bindings: remove everything the bindings targets generate -- the
# compiled module + CMake tree (bindings/build), the provisioned venv
# (bindings/.venv), the wheel output ($(WHEEL_OUT)), and the pytest/python
# caches. The next `make bindings` re-provisions from scratch. The top-level
# `clean`/`reset` already cover build/ and the venv; this is the bindings-only
# scoped equivalent.
clean-bindings:
@rm -rf bindings/build bindings/.venv $(WHEEL_OUT) \
bindings/.pytest_cache bindings/tests/__pycache__
@echo "clean-bindings: removed bindings build/, .venv/, $(WHEEL_OUT)/, caches"
# agent-hax: run the hax coding agent (https://github.com/shakfu/hax) against a
# chimera-hosted model. hax stays an external binary found on PATH (or via
# HAX=<path>) -- it is not vendored, linked, or version-pinned here. The two
# meet over loopback HTTP: scripts/agent_hax.sh starts `chimera serve` on a free
# port, waits for /health, points hax's llama.cpp provider at it, and kills
# the server when hax exits.
#
# make agent-hax MODEL=models/Qwen3-4B-Q8_0.gguf
# make agent-hax MODEL=models/Qwen3-4B-Q8_0.gguf HAX_ARGS='-p "list TODOs"'
# make agent-hax MODEL=... CHIMERA_SERVE_ARGS='--gpu-layers 99 -c 32768'
#
# The recipe forwards CHIMERA_SERVE_ARGS explicitly so it works whether it
# arrives from the environment, the make command line, or a target-specific
# assignment (as agent-hax-default uses; those are not auto-exported).
MODEL ?=
HAX_ARGS ?=
CHIMERA_SERVE_ARGS ?=
agent-hax: $(BUILD_DIR)/chimera
@[ -n "$(MODEL)" ] || { echo "usage: make agent-hax MODEL=path/to/model.gguf"; exit 1; }
@CHIMERA="$(BUILD_DIR)/chimera" CHIMERA_SERVE_ARGS="$(CHIMERA_SERVE_ARGS)" \
scripts/agent_hax.sh "$(MODEL)" $(HAX_ARGS)
# agent-hax-default: agent-hax with a known-good model and serve flags, so a
# session needs no arguments. LFM2.5-8B is a mixture-of-experts model with ~1B
# active parameters -- large enough to drive hax's tool protocol reliably (4B is
# the floor; a 1B model loops), while decoding at roughly 1B speed. -c 32768
# overrides chimera's n_ctx=0 default, which would request the model's full
# training context and size a KV cache that does not fit beside the weights.
#
# Target-specific variables propagate to prerequisites, so this sets MODEL and
# CHIMERA_SERVE_ARGS for the agent-hax run without duplicating its recipe.
# Override either with AGENT_MODEL= / AGENT_SERVE_ARGS=; HAX_ARGS still applies.
AGENT_MODEL ?= models/LFM2.5-8B-A1B-Q4_K_M.gguf
AGENT_SERVE_ARGS ?= --gpu-layers 99 -c 32768
agent-hax-default: MODEL := $(AGENT_MODEL)
agent-hax-default: CHIMERA_SERVE_ARGS := $(AGENT_SERVE_ARGS)
agent-hax-default: agent-hax
# test-golden: HTTP response-shape regression tests against fixed
# models. Spawns `chimera serve` on a free port, hits each route with a
# fixed payload, normalizes the response (redacting volatile fields),
# and diffs against checked-in goldens under tests/golden/. Catches the
# class of bug where a llama.cpp bump changes the JSON shape of a route
# we expose -- something `make test`'s did-it-work smoke can't see.
# Pass UPDATE_GOLDEN=1 to refresh the goldens (use when you legitimately
# changed a response shape and the new shape is correct).
test-golden: $(BUILD_DIR)/chimera
@$(PYTHON) scripts/test_golden.py