@bendyline/gezel-inference 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalog/chat-models/de/deepseek-v4.1-flash-552b-q2/manifest.json +120 -0
- package/dist/catalog/chat-models/de/deepseek-v4.1-flash-552b-q2/versions/1.0.0/manifest.json +21 -0
- package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/manifest.json +6 -5
- package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/versions/1.0.2/manifest.json +27 -0
- package/dist/catalog/chat-models/gl/glm-5.3-flash-320b-q2/versions/1.0.3/manifest.json +27 -0
- package/dist/catalog/chat-models/index.json +1 -1
- package/dist/catalog/chat-models/ne/nemotron3.5-bolt-74b-iq2/manifest.json +157 -0
- package/dist/catalog/chat-models/ne/nemotron3.5-bolt-74b-iq2/versions/1.0.0/manifest.json +30 -0
- package/dist/catalog/chat-models/ne/nemotron3.5-super-vl-120b-nvfp4/manifest.json +156 -0
- package/dist/catalog/chat-models/ne/nemotron3.5-super-vl-120b-nvfp4/versions/1.0.0/manifest.json +28 -0
- package/dist/catalog/chat-models/raw-index.json +1 -1
- package/dist/catalog/knowledge-catalogs/be/bendyline-how-to-books/keywords.json +56 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-alaska-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-nevada-part-0001/keywords.json +13 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/keywords.json +14 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-us-347-349/keywords.json +17 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-utah-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-utah-part-0002/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0002/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0003/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0004/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-washington-part-0005/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/ca/caselaw-wyoming-part-0001/keywords.json +12 -0
- package/dist/catalog/knowledge-catalogs/index.json +1 -1
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/keywords.json +21 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/keywords.json +27 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/keywords.json +23 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/keywords.json +26 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/keywords.json +28 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/keywords.json +29 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-dotnet-en/keywords.json +21 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/keywords.json +14 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/keywords.json +20 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-powershell-en/keywords.json +24 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-sql-server-en/keywords.json +27 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/keywords.json +18 -0
- package/dist/catalog/knowledge-catalogs/mi/microsoftdocs-windows-server-en/keywords.json +28 -0
- package/dist/catalog/knowledge-catalogs/raw-index.json +1 -1
- package/dist/catalog/knowledge-catalogs/re/regional-american-south/keywords.json +1700 -0
- package/dist/catalog/knowledge-catalogs/re/regional-american-south/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-andes-amazon/keywords.json +750 -0
- package/dist/catalog/knowledge-catalogs/re/regional-andes-amazon/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-australia-nz/keywords.json +1281 -0
- package/dist/catalog/knowledge-catalogs/re/regional-australia-nz/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-brazil/keywords.json +1125 -0
- package/dist/catalog/knowledge-catalogs/re/regional-brazil/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-britain-ireland/keywords.json +3287 -0
- package/dist/catalog/knowledge-catalogs/re/regional-britain-ireland/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-california-southwest/keywords.json +1835 -0
- package/dist/catalog/knowledge-catalogs/re/regional-california-southwest/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-canada-alaska/keywords.json +1184 -0
- package/dist/catalog/knowledge-catalogs/re/regional-canada-alaska/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-central-eastern-europe/keywords.json +1503 -0
- package/dist/catalog/knowledge-catalogs/re/regional-central-eastern-europe/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-asia/keywords.json +2836 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-asia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-southern-africa/keywords.json +813 -0
- package/dist/catalog/knowledge-catalogs/re/regional-east-southern-africa/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mediterranean/keywords.json +2082 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mediterranean/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mexico-central-america/keywords.json +1481 -0
- package/dist/catalog/knowledge-catalogs/re/regional-mexico-central-america/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-middle-east/keywords.json +1272 -0
- package/dist/catalog/knowledge-catalogs/re/regional-middle-east/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-midwest/keywords.json +758 -0
- package/dist/catalog/knowledge-catalogs/re/regional-midwest/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-north-africa/keywords.json +896 -0
- package/dist/catalog/knowledge-catalogs/re/regional-north-africa/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northeast/keywords.json +1281 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northeast/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-eurasia/keywords.json +346 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-eurasia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-europe/keywords.json +2688 -0
- package/dist/catalog/knowledge-catalogs/re/regional-northern-europe/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-islands/keywords.json +973 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-islands/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-northwest/keywords.json +937 -0
- package/dist/catalog/knowledge-catalogs/re/regional-pacific-northwest/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-rockies-plains/keywords.json +608 -0
- package/dist/catalog/knowledge-catalogs/re/regional-rockies-plains/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-south-asia/keywords.json +633 -0
- package/dist/catalog/knowledge-catalogs/re/regional-south-asia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southeast-asia/keywords.json +2697 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southeast-asia/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southern-cone/keywords.json +606 -0
- package/dist/catalog/knowledge-catalogs/re/regional-southern-cone/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-west-central-africa/keywords.json +564 -0
- package/dist/catalog/knowledge-catalogs/re/regional-west-central-africa/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe/keywords.json +3247 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-east/manifest.json +47 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-east/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-west/manifest.json +47 -0
- package/dist/catalog/knowledge-catalogs/re/regional-western-europe-west/versions/2026.10.5/manifest.json +133 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/keywords.json +5437 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-arts/versions/2026.10.1/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-astronomy/versions/2026.10.1/manifest.json +181 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-biology/versions/2026.10.1/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/keywords.json +5583 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-business/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/keywords.json +6386 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/manifest.json +2 -2
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-film-tv/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-food-drink/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/keywords.json +5672 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-games/versions/2026.10.1/manifest.json +137 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/manifest.json +68 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-geography/versions/2026.10.1/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/manifest.json +68 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-history/versions/2026.10.1/manifest.json +217 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-language/versions/2026.10.1/manifest.json +141 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-law/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/keywords.json +5719 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-literature/versions/2026.10.1/manifest.json +193 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-mathematics/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/keywords.json +16009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-medicine/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/keywords.json +5843 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-military/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/keywords.json +6264 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-music/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/manifest.json +66 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-arts/versions/2026.10.1/manifest.json +209 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/manifest.json +61 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-business/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/manifest.json +64 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-entertainment/versions/2026.10.1/manifest.json +201 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/manifest.json +60 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-military/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/manifest.json +55 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-other/versions/2026.10.1/manifest.json +165 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/manifest.json +63 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-politics/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/manifest.json +64 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-religion/versions/2026.10.1/manifest.json +201 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/manifest.json +65 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-science/versions/2026.10.1/manifest.json +205 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/manifest.json +73 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-people-sports/versions/2026.10.1/manifest.json +237 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/keywords.json +5447 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-politics/versions/2026.10.1/manifest.json +197 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-religion-philosophy/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/keywords.json +4009 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-science/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/keywords.json +5209 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-sports/versions/2026.10.1/manifest.json +193 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/keywords.json +6537 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-technology/versions/2026.10.1/manifest.json +185 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/keywords.json +5989 -0
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/manifest.json +1 -1
- package/dist/catalog/knowledge-catalogs/wi/wikipedia-transportation/versions/2026.10.1/manifest.json +189 -0
- package/dist/catalog/provenance.json +1 -1
- package/dist/engines/native-file-manifest.json +483 -413
- package/dist/engines/native-manifest.d.ts +4 -4
- package/dist/engines/native-manifest.js +36 -34
- package/dist/engines/resolver.js +3 -1
- package/dist/fs/atomic.d.ts +4 -1
- package/dist/fs/atomic.js +7 -1
- package/dist/http/openai-compat/chat-target.d.ts +1 -1
- package/dist/http/openai-compat/chat-target.js +1 -1
- package/dist/http/openai-compat/engine-failure.d.ts +25 -0
- package/dist/http/openai-compat/engine-failure.js +76 -0
- package/dist/http/openai-compat/responses-streaming.js +2 -1
- package/dist/http/openai-compat/responses-translate.d.ts +1 -1
- package/dist/http/openai-compat/translate.d.ts +1 -1
- package/dist/http/routes/engines.js +35 -0
- package/dist/http/routes/v1-chat.js +11 -2
- package/dist/http/routes/v1-knowledge.js +4 -0
- package/dist/http/routes/v1-models.js +6 -4
- package/dist/http/routes/v1-responses.d.ts +1 -1
- package/dist/http/routes/v1-responses.js +7 -1
- package/dist/inference-service.d.ts +6 -1
- package/dist/inference-service.js +20 -9
- package/dist/knowledge/catalog-host.d.ts +9 -1
- package/dist/knowledge/catalog-host.js +2 -0
- package/dist/knowledge/manager.d.ts +21 -0
- package/dist/knowledge/manager.js +85 -7
- package/dist/knowledge/search-worker.d.ts +1 -1
- package/dist/knowledge/spatial-query.d.ts +2 -3
- package/dist/knowledge/spatial-query.js +12 -6
- package/dist/knowledge/worker-host.js +2 -0
- package/dist/memory/embed-core.d.ts +16 -0
- package/dist/memory/embed-core.js +31 -0
- package/dist/memory/embed-worker.d.ts +7 -3
- package/dist/memory/embed-worker.js +13 -4
- package/dist/memory/embeddings.d.ts +17 -2
- package/dist/memory/embeddings.js +36 -7
- package/dist/models/ensure.d.ts +2 -0
- package/dist/models/ensure.js +9 -0
- package/dist/perf/responsiveness.d.ts +14 -1
- package/dist/perf/responsiveness.js +81 -10
- package/dist/providers/audio/kokoro-frontend.js +1 -1
- package/dist/providers/ds4/build-provider.js +6 -0
- package/dist/providers/llama-cpp/backend-health.d.ts +30 -0
- package/dist/providers/llama-cpp/backend-health.js +64 -0
- package/dist/providers/llama-cpp/backend-quarantine.d.ts +40 -0
- package/dist/providers/llama-cpp/backend-quarantine.js +100 -0
- package/dist/providers/llama-cpp/build-provider.js +247 -199
- package/dist/providers/llama-cpp/cache-adapter.d.ts +7 -1
- package/dist/providers/llama-cpp/cache-adapter.js +11 -1
- package/dist/providers/llama-cpp/devices.d.ts +22 -2
- package/dist/providers/llama-cpp/devices.js +48 -5
- package/dist/providers/llama-cpp/engine-flags.d.ts +0 -18
- package/dist/providers/llama-cpp/engine-flags.js +12 -18
- package/dist/providers/llama-cpp/gguf-metadata.js +20 -3
- package/dist/providers/llama-cpp/gpu-startup-watch.d.ts +41 -0
- package/dist/providers/llama-cpp/gpu-startup-watch.js +58 -0
- package/dist/providers/llama-cpp/models.d.ts +6 -0
- package/dist/providers/llama-cpp/models.js +23 -2
- package/dist/providers/llama-cpp/offload-planner.d.ts +26 -18
- package/dist/providers/llama-cpp/offload-planner.js +30 -14
- package/dist/providers/llama-cpp/provider.d.ts +22 -3
- package/dist/providers/llama-cpp/provider.js +39 -3
- package/dist/providers/mlx/provider.js +5 -6
- package/dist/providers/mlx/python/qwen4_ple.py +9 -2
- package/dist/providers/mlx/reasoning-stream.d.ts +15 -0
- package/dist/providers/mlx/reasoning-stream.js +35 -0
- package/dist/providers/native/capacity-broker.d.ts +21 -15
- package/dist/providers/native/capacity-broker.js +25 -13
- package/dist/providers/native/capacity-budget.d.ts +35 -3
- package/dist/providers/native/capacity-budget.js +49 -5
- package/dist/providers/native/device-capacity.d.ts +1 -1
- package/dist/providers/native/device-capacity.js +23 -5
- package/dist/providers/native/local-engine-runtime.js +31 -31
- package/dist/providers/native/measured-budget.js +2 -0
- package/dist/providers/native/supervisor.d.ts +25 -0
- package/dist/providers/native/supervisor.js +62 -8
- package/dist/providers/queue.d.ts +9 -2
- package/dist/providers/queue.js +17 -2
- package/dist/providers/session-hooks.d.ts +4 -1
- package/dist/system/amdgpu-adapters.d.ts +18 -0
- package/dist/system/amdgpu-adapters.js +55 -0
- package/dist/system/gpu-carve-out.d.ts +84 -0
- package/dist/system/gpu-carve-out.js +111 -0
- package/dist/system/memory.d.ts +17 -1
- package/dist/system/memory.js +94 -13
- package/dist/transformers-cache.d.ts +2 -0
- package/dist/transformers-cache.js +6 -3
- package/package.json +5 -5
|
@@ -123,7 +123,7 @@ export declare class LlamaCppCacheAdapter implements EngineCacheAdapter {
|
|
|
123
123
|
*/
|
|
124
124
|
private allocationTail;
|
|
125
125
|
private engineGeneration;
|
|
126
|
-
private
|
|
126
|
+
private slotCount;
|
|
127
127
|
private readonly resolveBaseUrl;
|
|
128
128
|
private readonly resolveAuthToken;
|
|
129
129
|
private readonly fetchImpl;
|
|
@@ -168,6 +168,12 @@ export declare class LlamaCppCacheAdapter implements EngineCacheAdapter {
|
|
|
168
168
|
*/
|
|
169
169
|
private slotActionsUnsupported;
|
|
170
170
|
constructor(opts: LlamaCppCacheAdapterOptions);
|
|
171
|
+
/**
|
|
172
|
+
* Follow an engine relaunched with a different `--parallel` count. Every
|
|
173
|
+
* binding goes with it: a session pinned to a slot the new engine lacks
|
|
174
|
+
* would send an `id_slot` the server rejects.
|
|
175
|
+
*/
|
|
176
|
+
setSlotCount(slotCount: number): void;
|
|
171
177
|
/** The provider survives engine restarts, but its resident slot contents do not. */
|
|
172
178
|
resetEngineState(): void;
|
|
173
179
|
/**
|
|
@@ -181,6 +181,15 @@ export class LlamaCppCacheAdapter {
|
|
|
181
181
|
throw new Error('Invalid llama disk cache budget');
|
|
182
182
|
this.diskCacheBudgetBytes = budget * 1024 * 1024;
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Follow an engine relaunched with a different `--parallel` count. Every
|
|
186
|
+
* binding goes with it: a session pinned to a slot the new engine lacks
|
|
187
|
+
* would send an `id_slot` the server rejects.
|
|
188
|
+
*/
|
|
189
|
+
setSlotCount(slotCount) {
|
|
190
|
+
this.slotCount = Math.max(1, Math.floor(slotCount));
|
|
191
|
+
this.resetEngineState();
|
|
192
|
+
}
|
|
184
193
|
/** The provider survives engine restarts, but its resident slot contents do not. */
|
|
185
194
|
resetEngineState() {
|
|
186
195
|
this.engineGeneration++;
|
|
@@ -306,7 +315,8 @@ export class LlamaCppCacheAdapter {
|
|
|
306
315
|
if (!entry || typeof entry !== 'object')
|
|
307
316
|
continue;
|
|
308
317
|
const slot = entry.id;
|
|
309
|
-
|
|
318
|
+
// The slot's resident tokens (prompt plus generated); there is no `n_cache_tokens`.
|
|
319
|
+
const cachedTokens = entry.n_prompt_tokens;
|
|
310
320
|
if (typeof slot !== 'number' || typeof cachedTokens !== 'number' || cachedTokens <= 0) {
|
|
311
321
|
continue;
|
|
312
322
|
}
|
|
@@ -31,6 +31,14 @@ export interface LlamaDeviceProbe {
|
|
|
31
31
|
probedAt: string;
|
|
32
32
|
/** True when returned from the on-disk cache rather than a fresh spawn. */
|
|
33
33
|
cached: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The RAM the OS reported when the probe ran. Changing an integrated GPU's
|
|
36
|
+
* firmware carve-out changes this figure and the GPU's own, so a mismatch
|
|
37
|
+
* means the cached devices describe a machine that no longer exists.
|
|
38
|
+
*/
|
|
39
|
+
systemRamBytes?: number;
|
|
40
|
+
/** {@link llamaBinaryFingerprint} of the build that answered. */
|
|
41
|
+
binary?: string;
|
|
34
42
|
}
|
|
35
43
|
export interface NvidiaRuntimeDevice {
|
|
36
44
|
index: number;
|
|
@@ -69,19 +77,31 @@ export declare function maxGpuVramBytes(devices: LlamaDevice[]): number;
|
|
|
69
77
|
* (e.g. "AMD Radeon RX 7900 XTX") in a tier rationale. Pure — unit-tested.
|
|
70
78
|
*/
|
|
71
79
|
export declare function pickBestGpuDevice(devices: LlamaDevice[]): LlamaDevice | null;
|
|
80
|
+
/**
|
|
81
|
+
* Which build a cached listing came from: its path (the backend) and its
|
|
82
|
+
* size and mtime (the release, since a native update rewrites the files).
|
|
83
|
+
* A listing taken from the CPU build, or from the build an update replaced,
|
|
84
|
+
* said "no GPU" about a 32 GB Radeon and was served for the rest of the day
|
|
85
|
+
* (2026-10-19).
|
|
86
|
+
*/
|
|
87
|
+
export declare function llamaBinaryFingerprint(binaryPath: string): string;
|
|
72
88
|
/**
|
|
73
89
|
* Probe device memory by running `<binaryPath> --list-devices`, caching
|
|
74
90
|
* the result. Reads the cache when present (VRAM capacity is stable per
|
|
75
|
-
* machine)
|
|
91
|
+
* machine), the OS still reports the RAM it was probed with, and the same
|
|
92
|
+
* build answered it; an empty listing is re-asked hourly. Pass `force` to
|
|
93
|
+
* re-probe. Any failure (spawn error, no
|
|
76
94
|
* cache, unparseable output) resolves to an empty device list — the
|
|
77
95
|
* planner then simply does nothing and the engine's own `--fit`/`-ngl
|
|
78
|
-
* auto` takes over. `nowIso`
|
|
96
|
+
* auto` takes over. `nowIso` and `systemRamBytes` are injectable for
|
|
97
|
+
* deterministic tests.
|
|
79
98
|
*/
|
|
80
99
|
export declare function probeLlamaDevices(opts: {
|
|
81
100
|
binaryPath: string;
|
|
82
101
|
home: string;
|
|
83
102
|
force?: boolean;
|
|
84
103
|
nowIso?: () => string;
|
|
104
|
+
systemRamBytes?: number;
|
|
85
105
|
}): Promise<LlamaDeviceProbe>;
|
|
86
106
|
/**
|
|
87
107
|
* Best-effort VRAM for a non-NVIDIA GPU (AMD Radeon, Intel Arc, any Vulkan
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* `backend.json` pattern in `core/native/llama-backend.ts`.
|
|
17
17
|
*/
|
|
18
18
|
import { execFile } from 'node:child_process';
|
|
19
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { totalmem } from 'node:os';
|
|
20
21
|
import { delimiter, dirname, join } from 'node:path';
|
|
21
22
|
import { promisify } from 'node:util';
|
|
22
23
|
import { windowsHeadlessSpawnOptions } from '@bendyline/gezel/native';
|
|
@@ -121,23 +122,59 @@ export function pickBestGpuDevice(devices) {
|
|
|
121
122
|
function cachePath(home) {
|
|
122
123
|
return join(home, 'engines', 'llama-cpp', 'devices.json');
|
|
123
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* An empty listing is re-asked after this long. A CPU build always answers
|
|
127
|
+
* "(none)", but so can a GPU build whose driver was mid-update, and a cached
|
|
128
|
+
* "no GPU" sizes every model against system RAM.
|
|
129
|
+
*/
|
|
130
|
+
const EMPTY_DEVICES_RETRY_MS = 60 * 60 * 1000;
|
|
131
|
+
/**
|
|
132
|
+
* Which build a cached listing came from: its path (the backend) and its
|
|
133
|
+
* size and mtime (the release, since a native update rewrites the files).
|
|
134
|
+
* A listing taken from the CPU build, or from the build an update replaced,
|
|
135
|
+
* said "no GPU" about a 32 GB Radeon and was served for the rest of the day
|
|
136
|
+
* (2026-10-19).
|
|
137
|
+
*/
|
|
138
|
+
export function llamaBinaryFingerprint(binaryPath) {
|
|
139
|
+
try {
|
|
140
|
+
const { size, mtimeMs } = statSync(binaryPath);
|
|
141
|
+
return `${binaryPath}|${size}|${Math.floor(mtimeMs)}`;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return binaryPath;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
124
147
|
/**
|
|
125
148
|
* Probe device memory by running `<binaryPath> --list-devices`, caching
|
|
126
149
|
* the result. Reads the cache when present (VRAM capacity is stable per
|
|
127
|
-
* machine)
|
|
150
|
+
* machine), the OS still reports the RAM it was probed with, and the same
|
|
151
|
+
* build answered it; an empty listing is re-asked hourly. Pass `force` to
|
|
152
|
+
* re-probe. Any failure (spawn error, no
|
|
128
153
|
* cache, unparseable output) resolves to an empty device list — the
|
|
129
154
|
* planner then simply does nothing and the engine's own `--fit`/`-ngl
|
|
130
|
-
* auto` takes over. `nowIso`
|
|
155
|
+
* auto` takes over. `nowIso` and `systemRamBytes` are injectable for
|
|
156
|
+
* deterministic tests.
|
|
131
157
|
*/
|
|
132
158
|
export async function probeLlamaDevices(opts) {
|
|
133
159
|
const { binaryPath, home, force } = opts;
|
|
134
160
|
const now = opts.nowIso ?? (() => new Date().toISOString());
|
|
161
|
+
const systemRamBytes = opts.systemRamBytes ?? totalmem();
|
|
135
162
|
const path = cachePath(home);
|
|
163
|
+
const binary = llamaBinaryFingerprint(binaryPath);
|
|
136
164
|
if (!force && existsSync(path)) {
|
|
137
165
|
try {
|
|
138
166
|
const cached = JSON.parse(readFileSync(path, 'utf8'));
|
|
139
|
-
|
|
167
|
+
// A cache without the RAM figure or the binary predates that check and
|
|
168
|
+
// re-probes once.
|
|
169
|
+
const emptyExpired = Array.isArray(cached.devices) &&
|
|
170
|
+
cached.devices.length === 0 &&
|
|
171
|
+
!(Date.parse(now()) - Date.parse(cached.probedAt) < EMPTY_DEVICES_RETRY_MS);
|
|
172
|
+
if (Array.isArray(cached.devices) &&
|
|
173
|
+
cached.systemRamBytes === systemRamBytes &&
|
|
174
|
+
cached.binary === binary &&
|
|
175
|
+
!emptyExpired) {
|
|
140
176
|
return { ...cached, cached: true };
|
|
177
|
+
}
|
|
141
178
|
}
|
|
142
179
|
catch {
|
|
143
180
|
// fall through to a fresh probe
|
|
@@ -165,7 +202,13 @@ export async function probeLlamaDevices(opts) {
|
|
|
165
202
|
// Leave devices empty — caller treats "no devices" as "no offload target".
|
|
166
203
|
return { devices, probedAt: now(), cached: false };
|
|
167
204
|
}
|
|
168
|
-
const probe = {
|
|
205
|
+
const probe = {
|
|
206
|
+
devices,
|
|
207
|
+
probedAt: now(),
|
|
208
|
+
cached: false,
|
|
209
|
+
systemRamBytes,
|
|
210
|
+
binary,
|
|
211
|
+
};
|
|
169
212
|
try {
|
|
170
213
|
mkdirSync(dirname(path), { recursive: true });
|
|
171
214
|
writeFileSync(path, JSON.stringify(probe, null, 2), 'utf8');
|
|
@@ -126,24 +126,6 @@ export interface EngineFlagInput {
|
|
|
126
126
|
* when the client also replays `reasoning_content`.
|
|
127
127
|
*/
|
|
128
128
|
reasoningPreserve?: boolean | undefined;
|
|
129
|
-
/**
|
|
130
|
-
* The model's GGUF `general.architecture`, for the Gemma-family auto
|
|
131
|
-
* defaults (sliding-window full cache). Undefined disables the arch
|
|
132
|
-
* heuristic; an explicit config/manifest `swaFull` still applies.
|
|
133
|
-
*/
|
|
134
|
-
architecture?: string | undefined;
|
|
135
|
-
/** The resolved model id, a fallback for `architecture` (see isGemmaModel). */
|
|
136
|
-
modelId?: string | undefined;
|
|
137
|
-
/**
|
|
138
|
-
* Whether resident weights plus the full SWA KV cache fit the host's
|
|
139
|
-
* fast-memory pool (VRAM on a discrete GPU). The exact estimate honors
|
|
140
|
-
* per-layer SWA dimensions and shared-KV layers. `false` declines the
|
|
141
|
-
* auto-default so a low-memory device keeps the model's full context on
|
|
142
|
-
* the windowed cache instead of spilling or OOMing. `undefined` (unknown
|
|
143
|
-
* host/metadata) and `true` retain the compatibility default. An explicit
|
|
144
|
-
* config/manifest `swaFull` wins regardless.
|
|
145
|
-
*/
|
|
146
|
-
swaFullAutoFits?: boolean | undefined;
|
|
147
129
|
/**
|
|
148
130
|
* The llama-server build's backend (`cuda`, `vulkan`, `metal`, `cpu`).
|
|
149
131
|
* Only a GPU backend can receive the GPU-resident thread default.
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
* dependency; the real `GezelConfig` / `LlamaCppEngineConfig` satisfy
|
|
22
22
|
* these shapes by construction.
|
|
23
23
|
*/
|
|
24
|
-
import { isGemmaModel } from './kv-cache-type.js';
|
|
25
24
|
/** Default `--cache-reuse` chunk when neither config nor manifest set it. */
|
|
26
25
|
export const DEFAULT_CACHE_REUSE = 256;
|
|
27
26
|
/**
|
|
@@ -101,7 +100,7 @@ function normalizeFlagKey(key) {
|
|
|
101
100
|
* `args` array.
|
|
102
101
|
*/
|
|
103
102
|
export function buildLlamaCppEngineArgs(input) {
|
|
104
|
-
const { config, perModel, planner, kvCacheType, slots, ggufHasMtp, installedDraftModelPath, reasoningFormat, reasoningPreserve,
|
|
103
|
+
const { config, perModel, planner, kvCacheType, slots, ggufHasMtp, installedDraftModelPath, reasoningFormat, reasoningPreserve, backend, hostCpuCount, } = input;
|
|
105
104
|
const args = [];
|
|
106
105
|
// ── GPU-layer offload (`--n-gpu-layers`) ──────────────────────────
|
|
107
106
|
const nGpuLayers = config.llamaCppNGpuLayers ?? perModel?.nGpuLayers ?? planner?.nGpuLayers;
|
|
@@ -176,26 +175,20 @@ export function buildLlamaCppEngineArgs(input) {
|
|
|
176
175
|
//
|
|
177
176
|
// So the engine already declines it precisely when it cannot honour it,
|
|
178
177
|
// which is what the warning is for. Withholding the flag ourselves only
|
|
179
|
-
// cost reuse on multi-slot dense engines.
|
|
180
|
-
//
|
|
181
|
-
// whether Gemma gets any cross-request prefix reuse at all.
|
|
178
|
+
// cost reuse on multi-slot dense engines. Gemma therefore gets it only
|
|
179
|
+
// with a pinned `--swa-full` below.
|
|
182
180
|
const explicitCacheReuse = config.llamaCppCacheReuse ?? perModel?.cacheReuse;
|
|
183
181
|
const cacheReuse = explicitCacheReuse ?? DEFAULT_CACHE_REUSE;
|
|
184
182
|
if (cacheReuse > 0)
|
|
185
183
|
args.push('--cache-reuse', String(cacheReuse));
|
|
186
184
|
// ── SWA full cache (`--swa-full`) ─────────────────────────────────
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
// fast memory, Gemma launches with the windowed cache instead.
|
|
195
|
-
const swaFull = config.llamaCppSwaFull ??
|
|
196
|
-
perModel?.swaFull ??
|
|
197
|
-
(swaFullAutoFits !== false && isGemmaModel({ architecture, modelId }));
|
|
198
|
-
if (swaFull)
|
|
185
|
+
// Opt-in only: an explicit config/manifest `swaFull`. Gemma used to get
|
|
186
|
+
// it by default for cross-request prefix reuse, until a paired A/B on
|
|
187
|
+
// gemma4-31b (2026-10-15) showed the full cache decodes 17-25% slower at
|
|
188
|
+
// every context depth, because decode reads each sliding-window layer's
|
|
189
|
+
// whole cache, and costs 51 GB more at 64K. Trials ran 19% longer in
|
|
190
|
+
// total despite the better reuse.
|
|
191
|
+
if (config.llamaCppSwaFull ?? perModel?.swaFull)
|
|
199
192
|
args.push('--swa-full');
|
|
200
193
|
// ── Thread / batch overrides ──────────────────────────────────────
|
|
201
194
|
// The GPU-resident default needs positive evidence that no model compute
|
|
@@ -248,7 +241,8 @@ export function buildLlamaCppEngineArgs(input) {
|
|
|
248
241
|
const nMax = config.llamaCppSpecDraftNMax ?? perModel?.spec?.nMax;
|
|
249
242
|
if (typeof nMax === 'number')
|
|
250
243
|
args.push('--spec-draft-n-max', String(nMax));
|
|
251
|
-
// v0.
|
|
244
|
+
// v0.7.0 supports probabilistic selection only for MTP/simple drafts
|
|
245
|
+
// (DFlash/LiLiCorr ignore it).
|
|
252
246
|
// Leave the upstream greedy default untouched unless explicitly set.
|
|
253
247
|
if ((safeSpecType === 'draft-mtp' || safeSpecType === 'draft-simple') &&
|
|
254
248
|
config.llamaCppSpecDraftSampling) {
|
|
@@ -32,8 +32,9 @@ const GgufValueType = {
|
|
|
32
32
|
INT64: 11,
|
|
33
33
|
FLOAT64: 12,
|
|
34
34
|
};
|
|
35
|
-
// `general.file_type` enum from llama.cpp/
|
|
36
|
-
//
|
|
35
|
+
// `general.file_type` enum (`llama_ftype`) from llama.cpp's include/llama.h.
|
|
36
|
+
// Copy new values from upstream rather than guessing: this table once mapped
|
|
37
|
+
// 30 to BF16 (upstream 30 is IQ4_XS, BF16 is 32) and had no NVFP4 at all.
|
|
37
38
|
// Anything unrecognised is reported by number.
|
|
38
39
|
const FILE_TYPE_NAMES = {
|
|
39
40
|
0: 'ALL_F32',
|
|
@@ -54,8 +55,24 @@ const FILE_TYPE_NAMES = {
|
|
|
54
55
|
18: 'MOSTLY_Q6_K',
|
|
55
56
|
19: 'MOSTLY_IQ2_XXS',
|
|
56
57
|
20: 'MOSTLY_IQ2_XS',
|
|
58
|
+
21: 'MOSTLY_Q2_K_S',
|
|
59
|
+
22: 'MOSTLY_IQ3_XS',
|
|
57
60
|
23: 'MOSTLY_IQ3_XXS',
|
|
58
|
-
|
|
61
|
+
24: 'MOSTLY_IQ1_S',
|
|
62
|
+
25: 'MOSTLY_IQ4_NL',
|
|
63
|
+
26: 'MOSTLY_IQ3_S',
|
|
64
|
+
27: 'MOSTLY_IQ3_M',
|
|
65
|
+
28: 'MOSTLY_IQ2_S',
|
|
66
|
+
29: 'MOSTLY_IQ2_M',
|
|
67
|
+
30: 'MOSTLY_IQ4_XS',
|
|
68
|
+
31: 'MOSTLY_IQ1_M',
|
|
69
|
+
32: 'MOSTLY_BF16',
|
|
70
|
+
36: 'MOSTLY_TQ1_0',
|
|
71
|
+
37: 'MOSTLY_TQ2_0',
|
|
72
|
+
38: 'MOSTLY_MXFP4_MOE',
|
|
73
|
+
39: 'MOSTLY_NVFP4',
|
|
74
|
+
40: 'MOSTLY_Q1_0',
|
|
75
|
+
41: 'MOSTLY_Q2_0',
|
|
59
76
|
};
|
|
60
77
|
/**
|
|
61
78
|
* The quantization tag a GGUF declares about itself, in the shape the model
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did a GPU build of llama-server actually get its GPU? Read from the
|
|
3
|
+
* engine's own startup output, because nothing else says.
|
|
4
|
+
*
|
|
5
|
+
* The bundled builds load their GPU backend with dlopen (`GGML_BACKEND_DL`),
|
|
6
|
+
* and in a release build ggml skips a backend that fails to load without a
|
|
7
|
+
* word. A CUDA backend that loads but cannot initialize prints one error line
|
|
8
|
+
* and registers no devices. Either way the engine starts, answers, and runs
|
|
9
|
+
* every token on the CPU — at a fraction of the speed the person's hardware
|
|
10
|
+
* and Settings promise — while gezel reports the CUDA or Vulkan build it
|
|
11
|
+
* launched.
|
|
12
|
+
*
|
|
13
|
+
* llama.cpp names every GPU it will use while loading a model
|
|
14
|
+
* (`using device CUDA0 (NVIDIA GB10) …`), integrated GPUs included. A GPU
|
|
15
|
+
* build that reached readiness after loading a model without that line ran
|
|
16
|
+
* without its GPU. The explicit lines say why when there is a why to say.
|
|
17
|
+
*/
|
|
18
|
+
export type GpuUnusedReason =
|
|
19
|
+
/** The CUDA runtime loaded and refused to start: `failed to initialize CUDA`. */
|
|
20
|
+
'init-failed'
|
|
21
|
+
/** The backend loaded and found no device it could use. */
|
|
22
|
+
| 'no-devices'
|
|
23
|
+
/** No sign of the GPU backend at all: it failed to load, silently. */
|
|
24
|
+
| 'not-loaded';
|
|
25
|
+
export interface GpuUnused {
|
|
26
|
+
backend: 'cuda' | 'vulkan';
|
|
27
|
+
reason: GpuUnusedReason;
|
|
28
|
+
/** The engine's own words, when it gave any. */
|
|
29
|
+
detail?: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class GpuStartupWatch {
|
|
32
|
+
private readonly backend;
|
|
33
|
+
private usingGpu;
|
|
34
|
+
private sawModelLoad;
|
|
35
|
+
private initFailure;
|
|
36
|
+
private noDevices;
|
|
37
|
+
constructor(backend: string | undefined);
|
|
38
|
+
observe(line: string): void;
|
|
39
|
+
/** At readiness: what kept the GPU out, or null when it is in use or this is no GPU build. */
|
|
40
|
+
verdict(): GpuUnused | null;
|
|
41
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Did a GPU build of llama-server actually get its GPU? Read from the
|
|
3
|
+
* engine's own startup output, because nothing else says.
|
|
4
|
+
*
|
|
5
|
+
* The bundled builds load their GPU backend with dlopen (`GGML_BACKEND_DL`),
|
|
6
|
+
* and in a release build ggml skips a backend that fails to load without a
|
|
7
|
+
* word. A CUDA backend that loads but cannot initialize prints one error line
|
|
8
|
+
* and registers no devices. Either way the engine starts, answers, and runs
|
|
9
|
+
* every token on the CPU — at a fraction of the speed the person's hardware
|
|
10
|
+
* and Settings promise — while gezel reports the CUDA or Vulkan build it
|
|
11
|
+
* launched.
|
|
12
|
+
*
|
|
13
|
+
* llama.cpp names every GPU it will use while loading a model
|
|
14
|
+
* (`using device CUDA0 (NVIDIA GB10) …`), integrated GPUs included. A GPU
|
|
15
|
+
* build that reached readiness after loading a model without that line ran
|
|
16
|
+
* without its GPU. The explicit lines say why when there is a why to say.
|
|
17
|
+
*/
|
|
18
|
+
const USING_GPU = /\busing device (?:CUDA|Vulkan|ROCm|SYCL|MTL)\d+\b/;
|
|
19
|
+
const CUDA_INIT_FAILED = /ggml_cuda_init: failed to initialize CUDA:\s*(.+?)\s*$/;
|
|
20
|
+
const VULKAN_NO_DEVICES = /ggml_vulkan: No devices found/;
|
|
21
|
+
/**
|
|
22
|
+
* Lines llama.cpp prints at the same verbosity as `using device`, while
|
|
23
|
+
* loading a model. Without one the output is too quiet to conclude anything.
|
|
24
|
+
*/
|
|
25
|
+
const MODEL_LOAD = /\b(?:llama_model_loader:|load_tensors:|print_info:|llama_model_load_from_file_impl:)/;
|
|
26
|
+
export class GpuStartupWatch {
|
|
27
|
+
backend;
|
|
28
|
+
usingGpu = false;
|
|
29
|
+
sawModelLoad = false;
|
|
30
|
+
initFailure;
|
|
31
|
+
noDevices = false;
|
|
32
|
+
constructor(backend) {
|
|
33
|
+
this.backend = backend;
|
|
34
|
+
}
|
|
35
|
+
observe(line) {
|
|
36
|
+
if (USING_GPU.test(line))
|
|
37
|
+
this.usingGpu = true;
|
|
38
|
+
if (MODEL_LOAD.test(line))
|
|
39
|
+
this.sawModelLoad = true;
|
|
40
|
+
const failure = CUDA_INIT_FAILED.exec(line);
|
|
41
|
+
if (failure?.[1])
|
|
42
|
+
this.initFailure = failure[1];
|
|
43
|
+
if (VULKAN_NO_DEVICES.test(line))
|
|
44
|
+
this.noDevices = true;
|
|
45
|
+
}
|
|
46
|
+
/** At readiness: what kept the GPU out, or null when it is in use or this is no GPU build. */
|
|
47
|
+
verdict() {
|
|
48
|
+
const backend = this.backend;
|
|
49
|
+
if ((backend !== 'cuda' && backend !== 'vulkan') || this.usingGpu)
|
|
50
|
+
return null;
|
|
51
|
+
if (backend === 'cuda' && this.initFailure) {
|
|
52
|
+
return { backend, reason: 'init-failed', detail: this.initFailure };
|
|
53
|
+
}
|
|
54
|
+
if (backend === 'vulkan' && this.noDevices)
|
|
55
|
+
return { backend, reason: 'no-devices' };
|
|
56
|
+
return this.sawModelLoad ? { backend, reason: 'not-loaded' } : null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -236,6 +236,11 @@ export interface LlamaCppModelManagerOptions {
|
|
|
236
236
|
engine: 'llama-cpp' | 'ds4';
|
|
237
237
|
id: string;
|
|
238
238
|
}) => void;
|
|
239
|
+
/**
|
|
240
|
+
* The llama-server backend installs are judged against for a catalog
|
|
241
|
+
* source's `excludeBackends`. Test seam — defaults to the process's own.
|
|
242
|
+
*/
|
|
243
|
+
resolveBackend?: () => string | undefined;
|
|
239
244
|
}
|
|
240
245
|
export declare class LlamaCppModelManager {
|
|
241
246
|
private readonly modelsRoot;
|
|
@@ -244,6 +249,7 @@ export declare class LlamaCppModelManager {
|
|
|
244
249
|
private readonly fetchImpl;
|
|
245
250
|
private readonly engine;
|
|
246
251
|
private readonly onInstalled?;
|
|
252
|
+
private readonly resolveBackend;
|
|
247
253
|
/**
|
|
248
254
|
* In-flight installs. `install()` registers an entry on first yield
|
|
249
255
|
* and clears it in the iterator's `finally` block, so consumers
|
|
@@ -35,13 +35,14 @@ import { createHash } from 'node:crypto';
|
|
|
35
35
|
import { createReadStream } from 'node:fs';
|
|
36
36
|
import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
37
37
|
import { basename, join, resolve } from 'node:path';
|
|
38
|
-
import { createLogger } from '@bendyline/gezel';
|
|
38
|
+
import { createLogger, llamaCppBackendExclusion, llamaCppBackendLabel } from '@bendyline/gezel';
|
|
39
39
|
import { listBundleModelFiles, publishStagedModel, safeBundleModelPath, } from '../../models/bundle-storage.js';
|
|
40
40
|
import { evaluateCatalogDrift } from '../../models/catalog-drift.js';
|
|
41
41
|
import { catalogPayloadFingerprint, describeCatalogPayload, } from '../../models/catalog-payload-identity.js';
|
|
42
42
|
import { MODEL_HASH_READ_BUFFER_BYTES, assertModelStorePathSafe, findModelRoot, findRenamedModelId, hashModelPayloadFiles, inspectModelDirectory, listIncompleteModelDownloads, listOverlayModelIds, makeSharedModelReadable, modelExistsOnlyReadOnly, modelStorageRoots, pruneModelPayloadFiles, readOnlyModelError, removeModelDir, verifyReadOnlyModelPayload, } from '../../models/storage-roots.js';
|
|
43
43
|
import { checkDiskSpace, describeDiskShortfall } from '../../utils/disk-space.js';
|
|
44
44
|
import { downloadWithRetry } from '../../utils/download-with-retry.js';
|
|
45
|
+
import { currentLlamaCppBackend } from './backend-health.js';
|
|
45
46
|
import { ggufQuantizationTag, readGgufSummary } from './gguf-metadata.js';
|
|
46
47
|
import { backfillGgufQuantization } from './gguf-quantization-backfill.js';
|
|
47
48
|
/**
|
|
@@ -58,6 +59,7 @@ export class LlamaCppModelManager {
|
|
|
58
59
|
fetchImpl;
|
|
59
60
|
engine;
|
|
60
61
|
onInstalled;
|
|
62
|
+
resolveBackend;
|
|
61
63
|
/**
|
|
62
64
|
* In-flight installs. `install()` registers an entry on first yield
|
|
63
65
|
* and clears it in the iterator's `finally` block, so consumers
|
|
@@ -78,6 +80,7 @@ export class LlamaCppModelManager {
|
|
|
78
80
|
this.catalog = opts.catalog;
|
|
79
81
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
80
82
|
this.onInstalled = opts.onInstalled;
|
|
83
|
+
this.resolveBackend = opts.resolveBackend ?? currentLlamaCppBackend;
|
|
81
84
|
}
|
|
82
85
|
/**
|
|
83
86
|
* The catalog source block this manager downloads from — `ds4` for the
|
|
@@ -435,6 +438,17 @@ export class LlamaCppModelManager {
|
|
|
435
438
|
};
|
|
436
439
|
return;
|
|
437
440
|
}
|
|
441
|
+
if (this.engine === 'llama-cpp') {
|
|
442
|
+
const backend = this.resolveBackend();
|
|
443
|
+
const excluded = llamaCppBackendExclusion(manifest.llamaCpp, backend);
|
|
444
|
+
if (excluded && backend) {
|
|
445
|
+
yield {
|
|
446
|
+
type: 'error',
|
|
447
|
+
error: `chat-model "${catalogId}" is not available on the ${llamaCppBackendLabel(backend)} llama.cpp engine: ${excluded}`,
|
|
448
|
+
};
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
438
452
|
tracked.totalBytes = installSrc.approxSizeBytes;
|
|
439
453
|
yield* this.runInstall(catalogId, manifest, tracked, skipSha, includeMmproj);
|
|
440
454
|
}
|
|
@@ -734,7 +748,10 @@ export class LlamaCppModelManager {
|
|
|
734
748
|
installed.fileSha256 = await hashModelPayloadFiles(itemDir, verifiedDigests);
|
|
735
749
|
await writeFile(join(itemDir, 'manifest.json'), JSON.stringify(installed, null, 2), 'utf8');
|
|
736
750
|
await makeSharedModelReadable(itemDir);
|
|
737
|
-
|
|
751
|
+
// ds4-server renders each family's prompt itself and never reads
|
|
752
|
+
// tokenizer.chat_template; DeepSeek V4.1 ships no Jinja template at all,
|
|
753
|
+
// and its first install warned that replies "may be incoherent".
|
|
754
|
+
const warning = summary.chatTemplateMissing && this.engine === 'llama-cpp'
|
|
738
755
|
? 'Model has no embedded chat template (tokenizer.chat_template is missing). llama-server will fall back to a generic template that may not match this model — replies may be incoherent. Consider a different quant of the same model.'
|
|
739
756
|
: undefined;
|
|
740
757
|
try {
|
|
@@ -964,6 +981,10 @@ includeMmproj = true) {
|
|
|
964
981
|
// after install without a second download.
|
|
965
982
|
if (src.draftModel) {
|
|
966
983
|
out.push({
|
|
984
|
+
...(src.draftModel.huggingfaceRepo
|
|
985
|
+
? { huggingfaceRepo: src.draftModel.huggingfaceRepo }
|
|
986
|
+
: {}),
|
|
987
|
+
...(src.draftModel.revision ? { revision: src.draftModel.revision } : {}),
|
|
967
988
|
repoPath: src.draftModel.filename,
|
|
968
989
|
destFilename: basename(src.draftModel.filename),
|
|
969
990
|
sha256: src.draftModel.sha256,
|
|
@@ -166,6 +166,29 @@ export declare function planMoeOffload(input: MoeOffloadInput): MoeOffloadDecisi
|
|
|
166
166
|
export declare function degradeMoeOffloadDecision(decision: MoeOffloadDecision | undefined): MoeOffloadDecision | null;
|
|
167
167
|
/** Dense counterpart of {@link degradeMoeOffloadDecision}. */
|
|
168
168
|
export declare function degradeDenseFfnOffloadDecision(decision: MoeOffloadDecision | undefined, blockCount: number | undefined): MoeOffloadDecision | null;
|
|
169
|
+
/**
|
|
170
|
+
* Room an extra engine slot must leave on the card. Other programs' graphics
|
|
171
|
+
* memory moves while a model runs (a browser, a video, the desktop), and a
|
|
172
|
+
* two-slot launch planned to within a gigabyte or two died with a Vulkan
|
|
173
|
+
* out-of-memory when they took more (2026-10-19, a 32 GB card).
|
|
174
|
+
*/
|
|
175
|
+
export declare const EXTRA_SLOT_GPU_CUSHION_BYTES: number;
|
|
176
|
+
/**
|
|
177
|
+
* Fewer slots before system RAM. Keep `slots` only when the model stays
|
|
178
|
+
* fully on the GPU with {@link EXTRA_SLOT_GPU_CUSHION_BYTES} to spare;
|
|
179
|
+
* otherwise drop to one slot when that alone keeps it fully on the GPU. A
|
|
180
|
+
* model that needs system RAM even at one slot keeps its slots and the
|
|
181
|
+
* planner's split. `plan` prices one slot count against one VRAM pool.
|
|
182
|
+
*/
|
|
183
|
+
export declare function shedSlotsForGpuResidency(input: {
|
|
184
|
+
slots: number;
|
|
185
|
+
vramBytes: number;
|
|
186
|
+
plan: (slots: number, vramBytes: number) => MoeOffloadDecision;
|
|
187
|
+
}): {
|
|
188
|
+
slots: number;
|
|
189
|
+
decision: MoeOffloadDecision;
|
|
190
|
+
shed: boolean;
|
|
191
|
+
};
|
|
169
192
|
export interface KvReserveInput {
|
|
170
193
|
blockCount?: number | undefined;
|
|
171
194
|
embeddingLength?: number | undefined;
|
|
@@ -286,10 +309,9 @@ export interface WindowedKvLinearization {
|
|
|
286
309
|
* than the layer ratio — global layers carry fewer KV heads at wider head
|
|
287
310
|
* dims (31b: 10 layers × 4 heads × 512+512 vs 50 layers × 16 heads ×
|
|
288
311
|
* 256+256) — so full-attention math overstates the windowed cache ~14×
|
|
289
|
-
* and using it for admission over-clamps or over-denies
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* this way).
|
|
312
|
+
* and using it for admission over-clamps or over-denies launches the
|
|
313
|
+
* windowed cache holds easily (the 2026-08-04 gemma4-26b sweep shipped
|
|
314
|
+
* 19–56K windows on a 65536 request this way).
|
|
293
315
|
*
|
|
294
316
|
* Returns undefined when the header lacks the SWA layout (no window, no
|
|
295
317
|
* pattern, length mismatches) so callers fall back to their conservative
|
|
@@ -323,17 +345,3 @@ export declare function estimateExactPerSlotKvBytesF16(input: Omit<WindowedKvInp
|
|
|
323
345
|
*/
|
|
324
346
|
export declare function estimateLinearHybridKvLinearization(input: WindowedKvInput): WindowedKvLinearization | undefined;
|
|
325
347
|
export declare function estimateWindowedKvLinearization(input: WindowedKvInput): WindowedKvLinearization | undefined;
|
|
326
|
-
export interface SwaFullFastMemoryFitInput {
|
|
327
|
-
residentWeightsBytes: number;
|
|
328
|
-
fullKvBytes: number;
|
|
329
|
-
/** Already safety-discounted fast-memory budget (VRAM on a discrete GPU). */
|
|
330
|
-
fastBudgetBytes: number;
|
|
331
|
-
/** Fast-memory reservations held by models that are already resident. */
|
|
332
|
-
committedOtherBytes?: number | undefined;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Whether Auto may keep `--swa-full` without spilling the model or KV cache
|
|
336
|
-
* out of the fast pool. The capacity budget already reserves driver/OS
|
|
337
|
-
* headroom, so this comparison must not discount it a second time.
|
|
338
|
-
*/
|
|
339
|
-
export declare function fitsSwaFullInFastMemory(input: SwaFullFastMemoryFitInput): boolean;
|
|
@@ -272,6 +272,33 @@ export function degradeDenseFfnOffloadDecision(decision, blockCount) {
|
|
|
272
272
|
}
|
|
273
273
|
return null;
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Room an extra engine slot must leave on the card. Other programs' graphics
|
|
277
|
+
* memory moves while a model runs (a browser, a video, the desktop), and a
|
|
278
|
+
* two-slot launch planned to within a gigabyte or two died with a Vulkan
|
|
279
|
+
* out-of-memory when they took more (2026-10-19, a 32 GB card).
|
|
280
|
+
*/
|
|
281
|
+
export const EXTRA_SLOT_GPU_CUSHION_BYTES = 2 * 1024 ** 3;
|
|
282
|
+
/**
|
|
283
|
+
* Fewer slots before system RAM. Keep `slots` only when the model stays
|
|
284
|
+
* fully on the GPU with {@link EXTRA_SLOT_GPU_CUSHION_BYTES} to spare;
|
|
285
|
+
* otherwise drop to one slot when that alone keeps it fully on the GPU. A
|
|
286
|
+
* model that needs system RAM even at one slot keeps its slots and the
|
|
287
|
+
* planner's split. `plan` prices one slot count against one VRAM pool.
|
|
288
|
+
*/
|
|
289
|
+
export function shedSlotsForGpuResidency(input) {
|
|
290
|
+
const decision = input.plan(input.slots, input.vramBytes);
|
|
291
|
+
const keep = { slots: input.slots, decision, shed: false };
|
|
292
|
+
if (input.slots <= 1 || input.vramBytes <= 0)
|
|
293
|
+
return keep;
|
|
294
|
+
const roomy = input.plan(input.slots, Math.max(0, input.vramBytes - EXTRA_SLOT_GPU_CUSHION_BYTES));
|
|
295
|
+
if (roomy.fullGpuResidency)
|
|
296
|
+
return keep;
|
|
297
|
+
const single = input.plan(1, input.vramBytes);
|
|
298
|
+
if (!single.fullGpuResidency)
|
|
299
|
+
return keep;
|
|
300
|
+
return { slots: 1, decision: single, shed: true };
|
|
301
|
+
}
|
|
275
302
|
/** Bytes per KV element for llama-server's `--cache-type-k/v` values. */
|
|
276
303
|
const KV_BYTES_PER_ELEMENT = {
|
|
277
304
|
f32: 4,
|
|
@@ -406,10 +433,9 @@ const SWA_UBATCH_MARGIN_TOKENS = 2048;
|
|
|
406
433
|
* than the layer ratio — global layers carry fewer KV heads at wider head
|
|
407
434
|
* dims (31b: 10 layers × 4 heads × 512+512 vs 50 layers × 16 heads ×
|
|
408
435
|
* 256+256) — so full-attention math overstates the windowed cache ~14×
|
|
409
|
-
* and using it for admission over-clamps or over-denies
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
* this way).
|
|
436
|
+
* and using it for admission over-clamps or over-denies launches the
|
|
437
|
+
* windowed cache holds easily (the 2026-08-04 gemma4-26b sweep shipped
|
|
438
|
+
* 19–56K windows on a 65536 request this way).
|
|
413
439
|
*
|
|
414
440
|
* Returns undefined when the header lacks the SWA layout (no window, no
|
|
415
441
|
* pattern, length mismatches) so callers fall back to their conservative
|
|
@@ -512,13 +538,3 @@ export function estimateWindowedKvLinearization(input) {
|
|
|
512
538
|
fixedBytes: Math.round(swaElemsPerToken * bytesPerElement * (slidingWindow + SWA_UBATCH_MARGIN_TOKENS) * loops),
|
|
513
539
|
};
|
|
514
540
|
}
|
|
515
|
-
/**
|
|
516
|
-
* Whether Auto may keep `--swa-full` without spilling the model or KV cache
|
|
517
|
-
* out of the fast pool. The capacity budget already reserves driver/OS
|
|
518
|
-
* headroom, so this comparison must not discount it a second time.
|
|
519
|
-
*/
|
|
520
|
-
export function fitsSwaFullInFastMemory(input) {
|
|
521
|
-
const required = Math.max(0, input.residentWeightsBytes) + Math.max(0, input.fullKvBytes);
|
|
522
|
-
const available = Math.max(0, input.fastBudgetBytes - Math.max(0, input.committedOtherBytes ?? 0));
|
|
523
|
-
return Number.isFinite(required) && Number.isFinite(available) && required <= available;
|
|
524
|
-
}
|